flutter开发仿抖音首页面上下滑动切换播放视频效果

深藏阁楼爱情的钟 2023-10-07 10:12 116阅读 0赞

题记
—— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天。

——-【视频教程 感兴趣的伙伴可以瞅瞅】


本小节讲述:
1 VideoPlayer 视频播放组件使用
2 VideoPlayerController 的使用分析
3 FutureBuilder 的使用分析
4 PageView构建上下滑动的整屏切换页面
5 TabBar 与 TabBarView 构建左右滑动切换的页面

在这里插入图片描述

1 首先我们来实现页面的主体部分

通过 TabBar 与 TabBarView 实现左右切换的页面

  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. class MainFind3Page extends StatefulWidget {
  4. @override
  5. State<StatefulWidget> createState() {
  6. return MainFindPage3State();
  7. }
  8. }
  9. class MainFindPage3State extends State with SingleTickerProviderStateMixin {
  10. List<String> tabTextList = ["关注", "推荐"];
  11. List<Tab> tabWidgetList = [];
  12. TabController tabController;
  13. @override
  14. void initState() {
  15. super.initState();
  16. for (var value in tabTextList) {
  17. tabWidgetList.add(Tab(
  18. text: "$value",
  19. ));
  20. }
  21. tabController = new TabController(length: tabTextList.length, vsync: this);
  22. }
  23. @override
  24. Widget build(BuildContext context) {
  25. return buildRootBody();
  26. }
  27. Widget buildRootBody() {
  28. return Scaffold(
  29. body: Stack(
  30. children: <Widget>[
  31. Positioned(
  32. left: 0,
  33. right: 0,
  34. top: 0,
  35. bottom: 0,
  36. child: Container(
  37. color: Colors.black,
  38. ),
  39. ),
  40. Positioned(
  41. left: 0,
  42. right: 0,
  43. top: 0,
  44. bottom: 0,
  45. child: buildTableViewWidget(),
  46. ),
  47. Positioned(
  48. left: 0,
  49. right: 0,
  50. bottom: 0,
  51. top: 54,
  52. child: buildTabBarWidget(),
  53. ),
  54. ],
  55. ),
  56. );
  57. }
  58. ///构建 TabBarView
  59. buildTableViewWidget() {
  60. return TabBarView(
  61. controller: tabController,
  62. children: tabTextList
  63. .map((value) => Container(
  64. alignment: Alignment.center,
  65. child: Text("$value",style: TextStyle(color: Colors.white),),
  66. ))
  67. .toList(),
  68. );
  69. }
  70. ///构建顶部标签部分
  71. buildTabBarWidget() {
  72. return Container(
  73. ///对齐在顶部中间
  74. alignment: Alignment.topCenter,
  75. child: TabBar(
  76. controller: tabController,
  77. tabs: tabWidgetList,
  78. ///指示器的颜色
  79. indicatorColor: Colors.white,
  80. ///指示器的高度
  81. indicatorWeight: 2.0,
  82. isScrollable: true,
  83. ///指示器的宽度与文字对齐
  84. indicatorSize: TabBarIndicatorSize.label,
  85. ),
  86. );
  87. }
  88. }

在这里是通过 帧布局 将 TabBar 与 TabBarView 叠在一起的。
效果如下
在这里插入图片描述

2 通过 PageView 来实现上下整屏切换效果

文章《flutter跨平台开发一点一滴分析系列文章》中 1.3.3 有记录 PageView 的使用案例

我们将上述 【构建 TabBarView】 处代码替换,使用 PageView 来构建 上下整屏页面切换效果

  1. ///构建 TabBarView
  2. buildTableViewWidget() {
  3. return TabBarView(
  4. controller: tabController,
  5. children: tabTextList
  6. .map((value) => buildTableViewItemWidget(value))
  7. .toList(),
  8. );
  9. }
  10. /// 用来创建上下滑动的页面
  11. Widget buildTableViewItemWidget(String value) {
  12. List<VideoModel> list =[];
  13. if(value == "推荐"){
  14. list= videoList;
  15. }else{
  16. list = videoList2;
  17. }
  18. return PageView.builder(
  19. /// pageview中 子条目的个数
  20. itemCount:list.length ,
  21. /// 上下滑动
  22. scrollDirection: Axis.vertical,
  23. itemBuilder: (BuildContext context,int index){
  24. VideoModel videoModel = list[index];
  25. return buildPageViewItemWidget(value,videoModel);
  26. });
  27. }

这里面用到了 videoList 与 videoList2,保存的数据模型,是在 initState函数中初始化的

  1. ///推荐模拟数据
  2. List <VideoModel> videoList =[];
  3. ///关注模拟数据
  4. List <VideoModel> videoList2 =[];
  5. @override
  6. void initState() {
  7. super.initState();
  8. ...
  9. ///创建模拟数据
  10. for (int i = 0; i < 10; i++) {
  11. VideoModel videoModel = new VideoModel();
  12. videoModel.videoName = "推荐测试数据$i";
  13. videoModel.pariseCount = i * 22;
  14. if (i % 3 == 0) {
  15. videoModel.isAttention = true;
  16. videoModel.isLike = true;
  17. } else {
  18. videoModel.isAttention = false;
  19. videoModel.isLike = false;
  20. }
  21. videoModel.videoImag ="";
  22. videoModel.videoUrl ="";
  23. videoList.add(videoModel);
  24. }
  25. for (int i = 0; i < 3; i++) {
  26. VideoModel videoModel = new VideoModel();
  27. videoModel.videoName = "关注测试数据$i";
  28. videoModel.pariseCount = i * 22;
  29. videoModel.isAttention = true;
  30. if (i % 3 == 0) {
  31. videoModel.isLike = true;
  32. } else {
  33. videoModel.isLike = false;
  34. }
  35. videoModel.videoImag ="";
  36. videoModel.videoUrl ="";
  37. videoList2.add(videoModel);
  38. }
  39. }

对于 VideoModel 来讲,就是我们保存视频信息的数据模型了

  1. class VideoModel {
  2. ///视频名称
  3. String videoName ='';
  4. ///视频链接
  5. String videoUrl ='';
  6. ///视频截图
  7. String videoImag ='';
  8. ///是否关注
  9. bool isAttention =false;
  10. ///关注的个数
  11. num attentCount =0;
  12. ///是否喜欢
  13. bool isLike = false;
  14. ///点赞的个数
  15. num pariseCount = 0;
  16. ///分享的次数
  17. num shareCount=0;
  18. }

在上述代码中我们也使用到了 buildPageViewItemWidget 函数,如下

  1. buildPageViewItemWidget(String value, VideoModel videoModel) {
  2. return FindVideoItemPage(value,videoModel);
  3. }

在这里直接构建 的 FindVideoItemPage ,看如下 FindVideoItemPage 的定义

  1. ///播放视频的页面
  2. class FindVideoItemPage extends StatefulWidget {
  3. String tabValue;
  4. VideoModel videoModel;
  5. FindVideoItemPage(this.tabValue, this.videoModel);
  6. @override
  7. State<StatefulWidget> createState() {
  8. return FindVideoItemPageState();
  9. }
  10. }
  11. class FindVideoItemPageState extends State<FindVideoItemPage> {
  12. ///创建视频播放控制 器
  13. VideoPlayerController videoPlayerController;
  14. ///控制更新视频加载初始化完成状态更新
  15. Future videoPlayFuture;
  16. @override
  17. void initState() {
  18. super.initState();
  19. videoPlayerController =
  20. VideoPlayerController.network(widget.videoModel.videoUrl);
  21. videoPlayFuture = videoPlayerController.initialize().then((_) {
  22. ///视频初始完成后
  23. ///调用播放
  24. videoPlayerController.play();
  25. setState(() {
  26. });
  27. });
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. return Stack(
  32. children: <Widget>[
  33. ///播放视频
  34. buildVideoWidget(),
  35. ///控制播放视频按钮
  36. buildControllWidget(),
  37. ///底部区域的视频介绍
  38. buildBottmFlagWidget(),
  39. ///右侧的用户信息按钮区域
  40. buildRightUserWidget(),
  41. ],
  42. );
  43. }
  44. @override
  45. void dispose() {
  46. super.dispose();
  47. videoPlayerController.dispose();
  48. }
  49. }

其实 FindVideoItemPage 就是我们 PageView 中构建的子视图了,我们可以看到 在 初始化函数 initState 中 创建了 VideoPlayerController,顾名思义 VideoPlayerController 是用来控制当前页面视频的播放的,在 dispose 中销毁 VideoPlayerController,这个也好理解,就是当前页面都释放掉了,播放的视频当然要停止播放了。

在这里这创建了一个 videoPlayFuture ,是用来监听 VideoPlayerController 初始化状态的,结合 FutureBuilder 来实时更新页面 State,如在方法 buildVideoWidget() 中

  1. ///播放视频
  2. buildVideoWidget() {
  3. return FutureBuilder(
  4. future: videoPlayFuture,
  5. builder: (BuildContext contex, value) {
  6. if (value.connectionState == ConnectionState.done) {
  7. ///点击事件
  8. return InkWell(
  9. onTap: () {
  10. if (videoPlayerController.value.initialized) {
  11. /// 视频已初始化
  12. if (videoPlayerController.value.isPlaying) {
  13. /// 正播放 --- 暂停
  14. videoPlayerController.pause();
  15. } else {
  16. ///暂停 ----播放
  17. videoPlayerController.play();
  18. }
  19. setState(() {
  20. });
  21. } else {
  22. ///未初始化
  23. videoPlayerController.initialize().then((_) {
  24. videoPlayerController.play();
  25. setState(() {
  26. });
  27. });
  28. }
  29. },
  30. ///居中
  31. child: Center(
  32. /// AspectRatio 组件用来设定子组件宽高比
  33. child: AspectRatio(
  34. ///设置视频的大小 宽高比。长宽比表示为宽高比。例如,16:9宽高比的值为16.0/9.0
  35. aspectRatio: videoPlayerController.value.aspectRatio,
  36. ///播放视频的组件
  37. child: VideoPlayer(videoPlayerController),
  38. ),
  39. ),
  40. );
  41. } else {
  42. return Container(
  43. alignment: Alignment.center,
  44. ///圆形加载进度
  45. child: CircularProgressIndicator(),
  46. );
  47. }
  48. },
  49. );
  50. }

FutureBuilder会依赖一个Future,对于FutureBuilder来讲

  1. FutureBuilder({
  2. this.future,
  3. this.initialData,
  4. @required this.builder,
  5. })

future ,FutureBuilder 中依赖的 Future ,通常是一个异步耗时任务,如这里的 videoPlayFuture 是指向 videoPlayerController 的初始化函数initialize(),这是一个异步的耗时操作,
builder ,Widget构建器,该构建器会在Future执行的不同阶段被多次调用,构建格式如下

  1. Function (BuildContext context, AsyncSnapshot snapshot)
  2. /**
  3. * snapshot会包含当前异步任务的状态信息及结果信息 ,
  4. * 比如我们可以通过snapshot.connectionState获取异步任务的状态信息、
  5. * 通过snapshot.hasError判断异步任务是否有错误等等
  6. */

而通过 snapshot.connectionState 获取的 ConnectionState 状态有以下值:

  1. enum ConnectionState {
  2. /// 当前没有异步任务,比如[FutureBuilder]的[future]为null时
  3. none,
  4. /// 异步任务处于等待状态
  5. waiting,
  6. /// Stream处于激活状态(流上已经有数据传递了),对于FutureBuilder没有该状态。
  7. active,
  8. /// 异步任务已经终止.
  9. done,
  10. }
3 通过 VideoPlayer 播放视频

使用 VideoPlayer,我们首先需要添加依赖

  1. video_player: ^0.6.4

对于 VideoPlayer 来讲,它只接收一个 VideoPlayerController,我们可以通过 VideoPlayerController 来绑定要播放的视频地址

  1. ///网络链接
  2. videoPlayerController = VideoPlayerController.network(widget.videoModel.videoUrl);
  3. ///本地链接
  4. VideoPlayerController videoPlayerController2 = VideoPlayerController.asset(widget.videoModel.videoUrl);
  5. ///File形式的视频
  6. VideoPlayerController videoPlayerController3 = VideoPlayerController.file(File(widget.videoModel.videoUrl));

当 绑定了播放的地址后,可以VideoPlayerController来预加载初始化播放器

  1. videoPlayerController.initialize().then((_) {
  2. ///视频初始完成后
  3. ///调用播放
  4. videoPlayerController.play();
  5. setState(() {
  6. });
  7. });

对于 initialize() 方法来讲,这是一个异步的耗时操作.

VideoPlayerValue 记录了当前视频播放的一些状态信息

  1. VideoPlayerValue videoPlayerValue = videoPlayerController.value;
  2. ///是否初始化完成
  3. bool initialized = videoPlayerValue.initialized;
  4. ///是否正在播放
  5. bool isPlaying = videoPlayerValue.isPlaying;
  6. ///当前播放的视频的宽高比例
  7. double aspectRatio = videoPlayerValue.aspectRatio;
  8. ///当前视频是否缓存
  9. bool isBuffer = videoPlayerValue.isBuffering;
  10. ///当前视频是否循环
  11. bool isLoop = videoPlayerValue.isLooping;
  12. ///当前播放视频的总时长
  13. Duration totalDuration = videoPlayerValue.duration;
  14. ///当前播放视频的位置
  15. Duration currentDuration = videoPlayerValue.position;

在这里,我们通过 totalDuration 与 currentDuration 就可实现播放进度的进度条绘制。


完毕

发表评论

表情:
评论列表 (有 0 条评论,116人围观)

还没有评论,来说两句吧...

相关阅读

    相关 vue+swiper仿视频滑动

    解释一番,本文章只是做了一个仿抖音的滑动效果,并没有添加其他效果,例如头部的类似关注与推荐功能、广告、右侧按钮等等,这些功能可按项目需求添加,在这里我就不多bb了。 先看下