Flame升级后调用SetState刷新Overlay引发游戏整体闪烁的解决方案咨询
解决Flame升级后Overlay刷新导致的屏幕闪烁问题
你的问题根源在于每次调用setState()都会重建整个Stack,包括里面的GameWidget——Flame的GameWidget在重建过程中会触发游戏画面的重新渲染,这就导致了300ms一次的闪烁。下面给你两种更优雅的解决思路:
方案一:使用Flame内置的Overlay系统(推荐)
Flame本身提供了游戏内的Overlay机制,完全不需要依赖Flutter的setState来更新UI,这样既能避免Widget树重建,也更符合Flame的游戏架构设计。
修改步骤:
- 在你的
BoatGame类中创建一个分数Overlay组件:
class ScoreOverlay extends OverlayWidget<BoatGame> { ScoreOverlay(BoatGame game) : super(game); @override Widget build(BuildContext context) { return Positioned( top: 20, left: 20, child: Text( "My Score: ${game.getScore()}", style: TextStyle(color: Colors.white, fontSize: 24), ), ); } }
- 在
BoatGame的onLoad方法中注册这个Overlay:
class BoatGame extends FlameGame { @override Future<void> onLoad() async { await super.onLoad(); addOverlay('score', (game) => ScoreOverlay(game)); } // 你的分数逻辑... int getScore() => _score; }
- 当分数更新时,直接通知Overlay刷新(不需要外部的
setState):
在游戏内部分数变化的地方,调用:
overlays.refresh('score');
这样只会刷新分数Overlay,不会影响游戏主画面,自然就不会闪烁了。
方案二:用ValueNotifier分离状态(保留Flutter Widget的方式)
如果你更习惯用Flutter的Widget来展示分数,可以用ValueNotifier来管理分数状态,让只有分数文本部分重建,而GameWidget保持稳定。
修改你的代码:
class _Boat extends State<Boat> with WidgetsBindingObserver { BoatGame game; final ValueNotifier<int> scoreNotifier = ValueNotifier(0); Timer? timer; @override void initState() { super.initState(); game = BoatGame(); refreshScore(); } refreshScore() async { timer = Timer.periodic(const Duration(milliseconds: 300), (timer) { if (mounted && game != null) { scoreNotifier.value = game.getScore(); } }); } @override void dispose() { timer?.cancel(); scoreNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Stack( children: [ GameWidget(game: game), ValueListenableBuilder<int>( valueListenable: scoreNotifier, builder: (context, score, child) { return Text("My Score: $score"); }, ), ], ); } }
这样每次分数更新时,只有ValueListenableBuilder内部的Text会重建,GameWidget不会被重新构建,也就不会出现闪烁了。
另外提醒一下:记得在dispose方法里取消Timer和释放ValueNotifier,避免内存泄漏哦。
内容的提问来源于stack exchange,提问作者PierrickG
相关产品推荐
相关产品推荐

