Flutter实现类Reels可滑动视频流:预加载与无缝播放方案咨询
Flutter Reels式视频流实现(带预加载)
核心实现思路
- 用
PageView实现垂直滑动的视频列表,每个页面对应一个视频 - 维护
VideoPlayerController数组,统一管理所有视频的加载/播放状态 - 监听当前视频的播放进度,当剩余时长≤10秒时,初始化并预加载下一个视频的控制器
- 页面切换时自动暂停上一个视频、播放当前视频,同时按需清理超出范围的控制器以节省资源
完整代码示例
import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; class ReelsVideoStream extends StatefulWidget { final List<String> videoUrls; // 传入20个视频的URL数组 const ReelsVideoStream({super.key, required this.videoUrls}); @override State<ReelsVideoStream> createState() => _ReelsVideoStreamState(); } class _ReelsVideoStreamState extends State<ReelsVideoStream> { late PageController _pageController; List<VideoPlayerController?> _controllers = []; int _currentPage = 0; bool _isLoadingNext = false; @override void initState() { super.initState(); _pageController = PageController(initialPage: 0); _initControllers(); _pageController.addListener(_onPageChanged); } // 初始化当前及下一个视频的控制器 void _initControllers() { _controllers = List.generate(widget.videoUrls.length, (index) => null); _initController(_currentPage); if (_currentPage + 1 < widget.videoUrls.length) { _initController(_currentPage + 1); } } // 初始化单个视频控制器 Future<void> _initController(int index) async { if (index < 0 || index >= widget.videoUrls.length || _controllers[index] != null) return; final controller = VideoPlayerController.networkUrl( Uri.parse(widget.videoUrls[index]), videoPlayerOptions: VideoPlayerOptions(mixWithOthers: false), ); setState(() { _controllers[index] = controller; }); await controller.initialize(); controller.setLooping(true); controller.addListener(() => _checkPreloadNext(index)); } // 触发下一个视频预加载的逻辑 void _checkPreloadNext(int currentIndex) { if (_isLoadingNext) return; final controller = _controllers[currentIndex]; if (controller == null || !controller.value.isPlaying) return; final duration = controller.value.duration; final position = controller.value.position; if (duration == null || position == null) return; final remaining = duration - position; if (remaining.inSeconds <= 10) { final nextIndex = currentIndex + 1; if (nextIndex < widget.videoUrls.length && _controllers[nextIndex] == null) { _isLoadingNext = true; _initController(nextIndex).then((_) { setState(() => _isLoadingNext = false); }); } } } // 页面切换时的状态处理 void _onPageChanged() { final newPage = _pageController.page?.round() ?? 0; if (newPage == _currentPage) return; // 切换播放/暂停状态 _controllers[_currentPage]?.pause(); _controllers[newPage]?.play(); // 预加载下一个视频 if (newPage + 1 < widget.videoUrls.length && _controllers[newPage + 1] == null) { _initController(newPage + 1); } // 销毁距离当前页面超过2位的控制器,释放资源 _disposeController(_currentPage - 2); _disposeController(newPage + 2); setState(() => _currentPage = newPage); } // 销毁单个控制器 void _disposeController(int index) { if (index < 0 || index >= widget.videoUrls.length) return; _controllers[index]?.dispose(); setState(() => _controllers[index] = null); } @override void dispose() { _pageController.dispose(); for (var controller in _controllers) { controller?.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: PageView.builder( controller: _pageController, scrollDirection: Axis.vertical, itemCount: widget.videoUrls.length, itemBuilder: (context, index) { final controller = _controllers[index]; if (controller == null || !controller.value.isInitialized) { return const Center(child: CircularProgressIndicator()); } return GestureDetector( onTap: () => controller.value.isPlaying ? controller.pause() : controller.play(), child: Stack( fit: StackFit.expand, children: [ VideoPlayer(controller), // 可自定义添加点赞、评论等UI组件 Positioned( bottom: 20, left: 20, child: Text( "视频 ${index + 1}/${widget.videoUrls.length}", style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ], ), ); }, ), ); } } // 使用示例 // void main() { // runApp(const MaterialApp( // home: ReelsVideoStream( // videoUrls: [ // "https://example.com/video1.mp4", // "https://example.com/video2.mp4", // // ... 补充剩余18个视频URL // ], // ), // )); // }
关键细节说明
- 资源管控:仅保留当前页面及前后1-2个视频的控制器,避免大量未使用的控制器占用内存
- 预加载时机:通过监听视频播放进度精准触发预加载,确保切换到下一个视频时已完成初始化
- 无缝体验:页面切换时自动同步播放状态,结合预加载实现无卡顿滑动
- 交互优化:支持点击切换播放/暂停,视频默认循环播放,符合Reels用户习惯
内容的提问来源于stack exchange,提问作者Naik Rahul
相关产品推荐
相关产品推荐

