Flutter 仿抖音视频滑动播放

手机软件开发 2024-9-21 11:22:13 35 0 来自 中国
这两天用Flutter实现了仿抖音视频滑动播放的功能

代码运行环境 flutter SDK 3.10.2
老例子先上效果(使用LICEcap截gif图总是有题目,来张静态的吧)
video_player: ^2.4.1一. video_player根本使用

/// 声明控制器late VideoPlayerController _controller;/// 初始化控制器_controller = VideoPlayerController.network(list[0]['video_url'])      ///设置视频循环播放      ..setLooping(true)      ///设置监听      ..addListener(() {        setState(() {        });      })      ///初始化      ..initialize().then((_) async {        ///初始化完成更新状态,否则播放器不会播放        setState(() {          playOrPauseVideo();        });      }).catchError((err) {        ///播放堕落        print(err);      });/// 体现视频SizedBox(     height: 240,     width: MediaQuery.of(context).size.width,     child: _controller.value.isInitialized              ? AspectRatio(                          aspectRatio:                          _controller.value.aspectRatio,                         child: VideoPlayer(_controller),                             )                             : const Text(                                    "没有要播放的视频",                                    style: TextStyle(color: Colors.red),                                ),                       ),注意点:在播放器initialize 完后肯定要更新播放器的状态,否则是widget拿不到状态改变,是不会播放的

二. 视频播放,停息

/// 判定播放和停息  void playOrPauseVideo() {    setState(() {      if (_controller.value.isPlaying) {        _controller.pause();      } else {        // If the video is paused, play it.        _controller.play();      }    });  }三. 视频全屏

视频全屏借助于auto_orientation来实现
auto_orientation: ^2.3.1代码实现
void _toggleFullScreen() {    setState(() {      if (_isFullScreen) {        /// 假如是全屏就切换竖屏        AutoOrientation.portraitAutoMode();        ///体现状态栏,与底部假造操纵按钮        SystemChrome.setEnabledSystemUIOverlays(            [SystemUiOverlay.top, SystemUiOverlay.bottom]);        _appbar = AppBar(          //标题居中          centerTitle: true,          title: const Text(            '仿抖音效果',            style:                TextStyle(overflow: TextOverflow.ellipsis, color: Colors.white),          ),          elevation: 0, //去掉Appbar底部阴影          //配景颜色          backgroundColor: Colors.blue,        );      } else {        AutoOrientation.landscapeAutoMode();        _appbar = null;        ///关闭状态栏,与底部假造操纵按钮        SystemChrome.setEnabledSystemUIOverlays([]);      }      _isFullScreen = !_isFullScreen;    });  }四. 体现加载进度

加载进度video_play已经封装好了VideoProgressIndicator,直接使用即可,将controller等其他参数设置好了就行.
Positioned(                    bottom: MediaQuery.of(context).padding.bottom,                    child: SizedBox(                      width: MediaQuery.of(context).size.width,                      height: 1,                      child: VideoProgressIndicator(                        _controller,                        allowScrubbing: true,                        padding: const EdgeInsets.all(0),                        colors: const VideoProgressColors(                          playedColor: Colors.white, // 已播放的颜色                          bufferedColor:                           Color.fromRGBO(255, 255, 255, .5), // 缓存中的颜色                          backgroundColor:                           Color.fromRGBO(255, 255, 255, .3), // 为缓存的颜色                        ),                      ),                    ))五. 循环播放视频,在设置的控制器的时间使用级联操纵符设置下就可以了

..setLooping(true)六. 实现抖音滑动效果

焦点原理就是使用PageView来实现的,须要注意的是每次滑动的时间须要将上一个_controller释放掉以后再重新创建一个,否则上个视频照旧会播放的.详细代码如下:
PageView.builder(          physics: const QuickerScrollPhysics(),          controller: _pageController,          scrollDirection: Axis.vertical,          itemCount: list.length,          onPageChanged: (index) {            _controller.dispose();            _controller =                VideoPlayerController.network(list[index]['video_url'])                  ..setLooping(true)                  ..addListener(() {                    setState(() {                    });                  })                  ..initialize().then((_) async {                    setState(() {                      playOrPauseVideo();                    });                  }).catchError((err) {                    print(err);                  });            if (index == list.length - 1) {              Future.delayed(                  const Duration(milliseconds: 200)).then((lwh) {                _pageController.jumpToPage(0);              });                  }          },          itemBuilder: (context, i) {            return Stack(              children: [                /// 播放器view                Container(                  color: Colors.black,                  child: Center(                    child: Stack(                      children: [                        AppNetImage(                          fit: BoxFit.fitWidth,                          imageUrl: list['image_url'],                          height: 240,                          width: MediaQuery.of(context).size.width,                        ),                        Positioned(                            child: Stack(                          children: [                            InkWell(                              child: SizedBox(                                height: 240,                                width: MediaQuery.of(context).size.width,                                child: _controller.value.isInitialized                                    ? AspectRatio(                                        aspectRatio:                                            _controller.value.aspectRatio,                                        child: VideoPlayer(_controller),                                      )                                    : const Text(                                        "没有要播放的视频",                                        style: TextStyle(color: Colors.red),                                      ),                              ),                              onTap: () {                                playOrPauseVideo();                              },                            ),                          ],                        )),                        Positioned(                          left: MediaQuery.of(context).size.width / 2 - 30,                          top: 90,                          child: _controller.value.isPlaying                              ? const SizedBox()                              : const Icon(                                  Icons.play_arrow,                                  color: Colors.white,                                  size: 60,                                ),                        ),                      ],                    ),                  ),                ),                /// 体现全屏按钮                Positioned(                    bottom: MediaQuery.of(context).padding.bottom + 100,                    right: 8,                    child: InkWell(                      child: const Icon(                        Icons.aspect_ratio,                        color: Colors.white,                        size: 30,                      ),                      onTap: () {                        _toggleFullScreen();                      },                    )),                /// 体现进度条                Positioned(                    bottom: MediaQuery.of(context).padding.bottom,                    child: SizedBox(                      width: MediaQuery.of(context).size.width,                      height: 1,                      child: VideoProgressIndicator(                        _controller,                        allowScrubbing: true,                        padding: const EdgeInsets.all(0),                        colors: const VideoProgressColors(                          playedColor: Colors.white, // 已播放的颜色                          bufferedColor:                           Color.fromRGBO(255, 255, 255, .5), // 缓存中的颜色                          backgroundColor:                           Color.fromRGBO(255, 255, 255, .3), // 为缓存的颜色                        ),                      ),                    ))              ],            );          },        )实现循环的效果就是当PageView滑动到末了一个时然后jumpToPage(0)来实现循环的.
if (index == list.length - 1) {              Future.delayed(                  const Duration(milliseconds: 200)).then((lwh) {                _pageController.jumpToPage(0);              });                  }七. 遗留题目待办理

当页面滑动到中心位置时视频播放会有题目,可以通过重写BouncingScrollPhysics的方法来办理.
八. 思索探索

之前用iOS来实现这个功能的时间接纳的是使用ScrollerView,然后创建三个子View,依次循环体现视频播放器View来实现这种效果,这样做的缘故起因就是为了性能来思量,不须要根据数据源去创建更多的View,flutter实现的话代码如下.
PageView(    scrollDirection: Axis.horizontal,    children: [      Container(        height: 300,        color: Colors.pink,        child: const Center(          child: Text("This is a page1"),        ),      ),      Container(        color: Colors.teal,        child: const Center(          child: Text("This is a page2"),        ),      ),      Container(        color: Colors.amber,        child: const Center(          child: Text("This is a page3"),        ),      ),    ],  );但是在flutter中PageView 提供了 builder方法,builder方法是懒加载的并不是一开始就创建大量的view,思绪和iOS的tableView大同小异.但是这两种方式哪种的服从更高,另有待考证.本日先写到这里,偶尔间再试下第二种思绪,检测下哪种更好吧.
九. 竣事

demo地点请移步: 项目地点
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-23 16:17, Processed in 0.116717 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表