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 <me@lukas.fyi>

Co-authored-by: Lukas Klingsbo <me@lukas.fyi>
This commit is contained in:
Erick
2022-03-18 14:45:21 -03:00
committed by GitHub
parent 4ddd90aafc
commit 6434829b45
4 changed files with 47 additions and 7 deletions

View File

@ -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

View File

@ -54,6 +54,7 @@ class Component {
ComponentSet? _children;
Completer<void>? _mountCompleter;
Completer<void>? _loadCompleter;
@protected
_LifecycleManager get lifecycle {
@ -208,6 +209,17 @@ class Component {
/// the lifetime of the [Component] object. Do not call this method manually.
Future<void>? onLoad() => null;
/// A future that will complete once this component has finished loading.
Future<void> get loaded {
if (isLoaded) {
return Future.value();
}
_loadCompleter ??= Completer<void>();
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;

View File

@ -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');

View File

@ -19,8 +19,6 @@ class MyGame extends FlameGame with TapDetector, DoubleTapDetector {
late final MinionComponent minionComponent;
bool loaded = false;
@override
Future<void> onLoad() async {
add(BGComponent());