如何使用GetX为Flutter视频播放器动态分配URL并更新UI
如何使用GetX为Flutter视频播放器动态分配URL并更新UI
看起来你遇到了两个头疼的问题——视频播放器得靠热重载才显示,还有没法从TopicView页面动态把视频URL传给控制器。别担心,咱们一步步把这些问题理顺,同时优化下代码结构。
第一步:重构VPlayerController,支持动态URL和自动UI更新
原来的控制器有几个小问题:硬编码了URL、chewieController不是响应式变量、初始化逻辑还重复了。咱们改成响应式的,再加个动态设置URL的方法:
class VPlayerController extends GetxController { late VideoPlayerController videoPlayerController; // 改成响应式变量,变化时自动触发UI更新 final Rx<ChewieController?> chewieController = Rx<ChewieController?>(null); // 记录当前URL,避免重复初始化相同视频 String? currentUrl; @override void onInit() { super.onInit(); } // 新增:动态设置视频URL的核心方法 Future<void> setVideoUrl(String url) async { // 相同URL直接返回,省得重复折腾 if (currentUrl == url) return; currentUrl = url; // 先释放旧资源,防止内存泄漏 if (videoPlayerController.value.isInitialized) { await videoPlayerController.dispose(); } chewieController.value?.dispose(); try { // 初始化新的视频控制器 videoPlayerController = VideoPlayerController.network(url); await videoPlayerController.initialize(); // 创建Chewie控制器并赋值给响应式变量 chewieController.value = ChewieController( videoPlayerController: videoPlayerController, autoPlay: false, looping: true, showControls: true, errorBuilder: (context, errorMessage) { return Center(child: Text(errorMessage)); }, autoInitialize: true, materialProgressColors: ChewieProgressColors( bufferedColor: primary.withOpacity(0.5), backgroundColor: primary.withOpacity(0.5), handleColor: primary, playedColor: primary, ), cupertinoProgressColors: ChewieProgressColors(), ); } catch (e) { Fluttertoast.showToast(msg: e.toString()); // 出错时清空控制器,避免UI卡住 chewieController.value = null; } } @override void onClose() { videoPlayerController.dispose(); chewieController.value?.dispose(); super.onClose(); } }
改动说明:
- 把
chewieController改成Rx响应式变量,不用手动调用update(),UI会自动跟着变化 - 移除了重复的初始化逻辑,换成专门的
setVideoUrl方法,处理旧资源释放和新视频初始化 - 加了
currentUrl判断,避免重复加载相同视频浪费资源 - 错误处理更稳妥,出错时清空控制器,防止UI显示异常
第二步:修改TopicView页面,传递URL并自动监听状态
原来的页面用GetBuilder,还没给控制器传URL,导致控制器不知道要播啥。咱们换成Obx监听响应式变量,同时在页面初始化时把URL传过去:
class TopicView extends StatelessWidget { final String? url; // 提前初始化控制器,整个页面生命周期只实例化一次 final VPlayerController _controller = Get.put(VPlayerController()); const TopicView({super.key, this.url}); @override Widget build(BuildContext context) { // 页面构建完成后再传URL,避免上下文问题 WidgetsBinding.instance.addPostFrameCallback((_) { if (url != null) { _controller.setVideoUrl(url!); } }); return Scaffold( appBar: AppBar(title: Text(widget.courseId)), // 注意这里的courseId要确保存在哦 body: SizedBox( width: ScreenManager.getWidth(context), height: ScreenManager.getHeight(context), child: Padding( padding: const EdgeInsets.all(AppPadding.p16), child: Column( children: [ Expanded( child: Obx(() { final chewieController = _controller.chewieController.value; final isInitialized = chewieController?.videoPlayerController.value.isInitialized ?? false; if (chewieController != null && isInitialized) { return Chewie(controller: chewieController); } else if (url == null) { return const Text('没有提供视频URL'); } else { return const Text('视频加载中...'); } }), ), ], ), ), ), ); } }
改动说明:
- 用
Get.put()提前初始化控制器,避免每次build都创建新实例 - 用
addPostFrameCallback确保页面渲染完成后再传URL,防止出现上下文错误 - 替换
GetBuilder为Obx,自动监听控制器的响应式变量变化,解决了热重载才显示的问题 - 增加了不同状态的UI提示:加载中、无URL、视频加载完成,用户体验更好
为啥原来的代码需要热重载才显示?
原来的GetBuilder需要控制器手动调用update()才能触发UI刷新,但你之前的控制器初始化完成后没调用这个方法,所以UI压根不知道视频已经准备好啦。改成响应式变量后,Obx会自动盯着变量变化,视频一初始化好就更新UI,再也不用依赖热重载了。
备注:内容来源于stack exchange,提问作者Jackson
相关产品推荐
相关产品推荐

