From 6434829b45cc131719fd950ef2d262d0bfbdff1b Mon Sep 17 00:00:00 2001 From: Erick Date: Fri, 18 Mar 2022 14:45:21 -0300 Subject: [PATCH] feat: adding loaded future to the component (#1466) * feat: adding loaded future to the component * fix: flame flare example game * docs * Update doc/flame/components.md Co-authored-by: Lukas Klingsbo Co-authored-by: Lukas Klingsbo --- doc/flame/components.md | 15 ++++++++---- .../flame/lib/src/components/component.dart | 13 ++++++++++ .../components/component_lifecycle_test.dart | 24 +++++++++++++++++++ packages/flame_flare/example/lib/main.dart | 2 -- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/doc/flame/components.md b/doc/flame/components.md index 46cb45de6..68063df63 100644 --- a/doc/flame/components.md +++ b/doc/flame/components.md @@ -28,14 +28,19 @@ the `Component` remove method. The `onLoad` method can be overridden to run asynchronous initialization code for the component, like loading an image for example. This method is executed after `onGameResize`, but before `onMount`. This method is guaranteed to execute only once during the lifetime of the component, so -you can think of it as an "asynchronous constructor". +you can think of it as an "asynchronous constructor". -The `onMount` method runs every time when the component is mounted into a game tree. This means that +The `onMount` method runs every time when the component is mounted into a game tree. This means that you should not initialize `late final` variables here, since this method might run several times throughout the component's lifetime. This method will only run if the parent is already mounted. If the parent is not mounted yet, then this method will wait in a queue (this will have no effect -on the rest of the game engine). +on the rest of the game engine). +A component lifecycle state can be checked by a series of getters: + - `isLoaded`: Returns a bool with the current loaded state + - `loaded`: Returns a future that will complete once the component has finished loading + - `isMounted`: Returns a bool with the current mounted state + - `mounted`: Returns a future that will complete once the component has finished mounting ### Priority @@ -62,14 +67,14 @@ class MyGame extends FlameGame { } ``` -To update the priority of a component you have to either just set it to a new value, like +To update the priority of a component you have to either just set it to a new value, like `component.priority = 2`, and it will be updated in the next tick. Example: ```dart class MyComponent extends PositionComponent with Tappable { - + MyComponent() : super(priority: 1); @override diff --git a/packages/flame/lib/src/components/component.dart b/packages/flame/lib/src/components/component.dart index eb0ec5a70..1bde7539e 100644 --- a/packages/flame/lib/src/components/component.dart +++ b/packages/flame/lib/src/components/component.dart @@ -54,6 +54,7 @@ class Component { ComponentSet? _children; Completer? _mountCompleter; + Completer? _loadCompleter; @protected _LifecycleManager get lifecycle { @@ -208,6 +209,17 @@ class Component { /// the lifetime of the [Component] object. Do not call this method manually. Future? onLoad() => null; + /// A future that will complete once this component has finished loading. + Future get loaded { + if (isLoaded) { + return Future.value(); + } + + _loadCompleter ??= Completer(); + + return _loadCompleter!.future; + } + /// Called when the component is added to its parent. /// /// This method only runs when the component is fully loaded, i.e. after @@ -420,6 +432,7 @@ class Component { } else { return onLoadFuture.then((_) { _state = LifecycleState.loaded; + _loadCompleter?.complete(); }); } return null; diff --git a/packages/flame/test/components/component_lifecycle_test.dart b/packages/flame/test/components/component_lifecycle_test.dart index e23dcbfb1..8b3568829 100644 --- a/packages/flame/test/components/component_lifecycle_test.dart +++ b/packages/flame/test/components/component_lifecycle_test.dart @@ -93,6 +93,30 @@ void main() { }, ); + flameGame.test('component loaded completes', (game) async { + final component = _MyComponent(); + await game.add(component); + final loaded = component.loaded; + + await game.ready(); + + return expectLater(loaded, completes); + }); + + flameGame.test( + 'component loaded completes even after the ' + 'component is already loaded', + (game) async { + final component = _MyComponent(); + await game.add(component); + await game.ready(); + + final loaded = component.loaded; + + return expectLater(loaded, completes); + }, + ); + // Obsolete scenario, when we used to have a separate "prepare" stage flameGame.test('parent prepares the component', (game) async { final parent = _MyComponent('parent'); diff --git a/packages/flame_flare/example/lib/main.dart b/packages/flame_flare/example/lib/main.dart index 566f9ca25..2d340f3e5 100644 --- a/packages/flame_flare/example/lib/main.dart +++ b/packages/flame_flare/example/lib/main.dart @@ -19,8 +19,6 @@ class MyGame extends FlameGame with TapDetector, DoubleTapDetector { late final MinionComponent minionComponent; - bool loaded = false; - @override Future onLoad() async { add(BGComponent());