Skip to content

fix!: Wait for the whole component tree to load before starting the game - #3998

Open
spydon wants to merge 2 commits into
mainfrom
fix/wait-for-full-tree-load
Open

fix!: Wait for the whole component tree to load before starting the game#3998
spydon wants to merge 2 commits into
mainfrom
fix/wait-for-full-tree-load

Conversation

@spydon

@spydon spydon commented Aug 14, 2026

Copy link
Copy Markdown
Member

Description

Previously the GameWidget only awaited the game's own onLoad before removing the loading widget and starting the game. Components added during the initial load (the world, the camera, and anything added in onLoad) were still loading in the lifecycle queue when the first frames ran, so a loadingBuilder disappeared too early and the game started half-materialized.

With this PR the GameWidget waits for the whole initial component tree to be loaded and mounted before the game attaches and starts ticking:

  • Game gained a ready() method that the GameWidget awaits after load() and mount(), before the first update. It is a no-op by default and overridden by FlameGame.
  • FlameGame.ready() no longer polls with zero-delay timers. It now drains the lifecycle queue event-driven, sleeping on a new internal Component.loadSettled future (which completes on load success or failure, without interfering with how load errors are reported through Component.loaded or the current Zone) and on a new internal queue mutation notification, so it also wakes up when a stuck component is removed while loading. This also makes ready() work under testWidgets' fake async, where zero-delay timer polling either stalls or spins.
  • A component that fails to load still does not block the game start, its error reporting is unchanged.
  • The loaderFuture in GameWidget now stops early if the widget is disposed or the game instance is swapped out while the game is still loading, instead of continuing to mount components into a disposed game.
  • dequeueAdd/dequeueRemove now traverse the queue with forEachWhere instead of the shared iterator, since they can be called from user code that runs while processLifecycleEvents is iterating (the queue only supports one iteration at a time).
  • flame_test's initializeGame and the benchmark helper boot games the same way, so tests and benchmarks match the production startup path.

Note that a component whose initial onLoad never completes will now keep the game on the loading widget instead of starting without it, which is the intent of this change. Removing such a component unblocks the start.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [-] I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Migration instructions

The game no longer starts while components added during the initial load are still loading:

  • If you relied on the game rendering and updating while slow components were still loading (for example an in-game loading indicator implemented as a component), move that indicator to the GameWidget.loadingBuilder, or add the slow components after the initial load instead of during onLoad.
  • A component whose onLoad awaits something that only happens once the game is running (an update tick, an overlay interaction) will now keep the game on the loading widget forever; restructure such loads so they complete on their own.
  • Widget tests that pump a GameWidget may need an extra tester.pump() before the game is attached, since the loader now also waits for the component tree.
  • If your Game subclass defines its own ready() member, it now overrides Game.ready() and is awaited by the GameWidget before the game starts.

Related Issues

Closes #3997

@spydon
spydon marked this pull request as ready for review August 14, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The whole component tree should be waited for in the initial onLoad in the FlameGame.

1 participant