Flutter调用StatefulWidget的setState时YoutubePlayer等无关组件刷新如何解决
目标
调用setState时不重绘其他无关组件
实现步骤
我使用youtube_player_flutter插件播放YouTube视频,初始化代码如下:
String id; YoutubePlayerController _youtubeController = YoutubePlayerController( initialVideoId: id, flags: YoutubePlayerFlags(....); // 初始化后调用组件: YoutubePlayer( controller: _youtubeController, showVideoProgressIndicator: true, ),
同一个StatefulWidget中包含Text、ElevatedButton和YoutubePlayer组件,布局代码如下:
String str = "abc"; // 放在Column或Row中 Text(str), ElevatedButton(child: Text("点击"), onPressed: () { setState((){ str = "ABC" });}), YoutubePlayer( controller: _youtubeController, showVideoProgressIndicator: true, ),
遇到的问题
点击按钮调用setState更新文本时,YoutubePlayer也会被刷新,导致视频重新加载、播放进度被重置。由于initialVideoId是动态不确定的,我无法将YoutubePlayer定义为const组件避免重绘。
flutter doctor 环境信息
[√] Flutter (Channel beta, 2.6.0-5.2.pre, on Microsoft Windows [Version 10.0.22000.282], locale ja-JP) • Flutter version 2.6.0-5.2.pre at D:\src\flutter_windows_2.5.0-stable\flutter • Framework revision 400608f101 (7 weeks ago), 2021-09-15 15:50:26 -0700 • Engine revision 1d521d89d8 • Dart version 2.15.0 (build 2.15.0-82.2.beta) [√] Android toolchain - develop for Android devices (Android SDK version 29.0.3) • Android SDK at D:\Android • Platform android-30, build-tools 29.0.3 • Java binary at: D:\Program Files\Android\Android Studio\jre\bin\java • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6842174) • All Android licenses accepted. [√] Chrome - develop for the web • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe [√] Android Studio (version 4.2) • Android Studio at D:\Program Files\Android\Android Studio • Flutter、Dart插件可直接在IDE插件市场安装 • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6842174) [√] VS Code (version 1.61.2) • VS Code at C:\Users\yukik\AppData\Local\Programs\Microsoft VS Code • Flutter extension version 3.27.0 [√] Connected device (3 available) • Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator) • Chrome (web) • chrome • web-javascript • Google Chrome 95.0.4638.54 • Edge (web) • edge • web-javascript • Microsoft Edge 95.0.1020.40 • No issues found!
解决方案
方案1:将局部更新区域抽离为独立Stateful组件
把需要刷新的Text和按钮部分单独封装成自定义的StatefulWidget,YoutubePlayer保留在父组件中。子组件内部调用setState只会刷新自身区域,不会触发父组件重绘,自然不会影响YoutubePlayer的状态。
示例代码:
// 抽离的子组件 class TextUpdateWidget extends StatefulWidget { const TextUpdateWidget({super.key}); @override State<TextUpdateWidget> createState() => _TextUpdateWidgetState(); } class _TextUpdateWidgetState extends State<TextUpdateWidget> { String str = "abc"; @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Text(str), ElevatedButton( child: const Text("点击"), onPressed: () { setState((){ str = "ABC"; }); }, ), ], ); } } // 父组件的build方法 @override Widget build(BuildContext context) { return Column( children: [ const TextUpdateWidget(), YoutubePlayer( controller: _youtubeController, showVideoProgressIndicator: true, ), ], ); }
方案2:使用ValueNotifier+ValueListenableBuilder实现局部刷新
无需拆分组件,通过值监听的方式只更新需要变化的文本节点,全程不需要调用全局setState,整个页面除了目标Text外都不会重绘。
示例代码:
// 在State类中定义ValueNotifier final ValueNotifier<String> _strNotifier = ValueNotifier("abc"); // 布局部分写法 Column( children: [ ValueListenableBuilder<String>( valueListenable: _strNotifier, builder: (context, value, child) { return Text(value); }, ), ElevatedButton( child: const Text("点击"), onPressed: () { // 直接更新notifier的值,不需要setState _strNotifier.value = "ABC"; }, ), YoutubePlayer( controller: _youtubeController, showVideoProgressIndicator: true, ), ], ) // 页面销毁时记得释放notifier @override void dispose() { _strNotifier.dispose(); super.dispose(); }
补充注意点
如果后续需要动态更新视频ID,不要重新创建YoutubePlayerController,直接调用控制器的load()方法传入新的视频ID即可,避免重置播放器状态:
_youtubeController.load('新的视频ID');
内容的提问来源于stack exchange,提问作者Beginner_
相关产品推荐
相关产品推荐

