From ee7dcd270e28a98a7359516fdd7128cbf84a4032 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Fri, 26 Dec 2025 16:11:57 +0900 Subject: [PATCH] =?UTF-8?q?fix(animation):=20WASM=20=EB=AA=A8=EB=93=9C=20?= =?UTF-8?q?=EC=95=88=EC=A0=95=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SchedulerBinding으로 프레임 빌드 중 setState 방지 - persistentCallbacks 단계에서 addPostFrameCallback으로 지연 처리 --- .../front/widgets/hero_vs_boss_animation.dart | 36 ++++++++++++++++--- .../new_character/widgets/race_preview.dart | 15 +++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/src/features/front/widgets/hero_vs_boss_animation.dart b/lib/src/features/front/widgets/hero_vs_boss_animation.dart index 5bec0b5..d73ca11 100644 --- a/lib/src/features/front/widgets/hero_vs_boss_animation.dart +++ b/lib/src/features/front/widgets/hero_vs_boss_animation.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:askiineverdie/data/race_data.dart'; import 'package:askiineverdie/src/core/animation/front_screen_animation.dart'; @@ -45,7 +46,19 @@ class _HeroVsBossAnimationState extends State { _animationTimer = Timer.periodic( const Duration(milliseconds: frontScreenAnimationIntervalMs), (_) { - if (mounted) { + if (!mounted) return; + // 프레임 빌드 중이면 다음 프레임까지 대기 + if (SchedulerBinding.instance.schedulerPhase == + SchedulerPhase.persistentCallbacks) { + SchedulerBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() { + _currentFrame = + (_currentFrame + 1) % frontScreenAnimationFrameCount; + }); + } + }); + } else { setState(() { _currentFrame = (_currentFrame + 1) % frontScreenAnimationFrameCount; @@ -69,10 +82,23 @@ class _HeroVsBossAnimationState extends State { final allRaces = RaceData.all; if (allRaces.isEmpty) return; - setState(() { - _raceIndex = (_raceIndex + 1) % allRaces.length; - _currentRaceId = allRaces[_raceIndex].raceId; - }); + // 프레임 빌드 중이면 다음 프레임까지 대기 + if (SchedulerBinding.instance.schedulerPhase == + SchedulerPhase.persistentCallbacks) { + SchedulerBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() { + _raceIndex = (_raceIndex + 1) % allRaces.length; + _currentRaceId = allRaces[_raceIndex].raceId; + }); + } + }); + } else { + setState(() { + _raceIndex = (_raceIndex + 1) % allRaces.length; + _currentRaceId = allRaces[_raceIndex].raceId; + }); + } } /// 글리치 효과: 랜덤 문자 대체 diff --git a/lib/src/features/new_character/widgets/race_preview.dart b/lib/src/features/new_character/widgets/race_preview.dart index 510357b..e9324b1 100644 --- a/lib/src/features/new_character/widgets/race_preview.dart +++ b/lib/src/features/new_character/widgets/race_preview.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:askiineverdie/src/core/animation/character_frames.dart'; import 'package:askiineverdie/src/core/animation/race_character_frames.dart'; @@ -48,7 +49,19 @@ class _RacePreviewState extends State { void _startAnimation() { _timer = Timer.periodic(const Duration(milliseconds: 400), (_) { - if (mounted) { + if (!mounted) return; + + // 프레임 빌드 중이면 다음 프레임까지 대기 (WASM 모드 안정성) + if (SchedulerBinding.instance.schedulerPhase == + SchedulerPhase.persistentCallbacks) { + SchedulerBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() { + _currentFrame++; + }); + } + }); + } else { setState(() { _currentFrame++; });