Skip to content

Commit

Permalink
feat: Add listener for initial state on flame_bloc (#2382)
Browse files Browse the repository at this point in the history
While analyzing the tests for flame_bloc, I stumbled upon an interesting conundrum.
It seems that the initialState provided does not trigger a state change callback through onNewState. While that is in line with bloc, I think for most game use cases it is important to keep track of the "last" state of stuff - and in fact most of our existing examples do use the library in that way, to keep track of the last (i.e., the current) state.
However, doing so generically is not simple with the current library because of how the initial state is not provided. While you could initialize your last manually, if there is a dynamic initial state, it would not be trivial to wire it up.
This, therefore, adds a new listener of onInitialState, maintaining the behaviour of onNewState.

Note: this solves the last unused reference from this issue by using it on the new tests.
  • Loading branch information
luanpotter committed May 1, 2023
1 parent 532899c commit 01121c2
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/flame_bloc/lib/src/flame_bloc_listenable.dart
Expand Up @@ -50,6 +50,7 @@ mixin FlameBlocListenable<B extends BlocBase<S>, S> on Component {
}
_bloc = bloc;
_state = bloc.state;
onInitialState(_state);
_subscription = bloc.stream.listen((newState) {
if (_state != newState) {
final callNewState = listenWhen(_state, newState);
Expand All @@ -73,6 +74,11 @@ mixin FlameBlocListenable<B extends BlocBase<S>, S> on Component {
/// Default implementation is a no-op.
void onNewState(S state) {}

/// Called only once with the initial state provided.
///
/// Default implementation is a no-op.
void onInitialState(S state) {}

@override
@mustCallSuper
void onRemove() {
Expand Down
18 changes: 16 additions & 2 deletions packages/flame_bloc/test/src/flame_bloc_listenable_test.dart
Expand Up @@ -16,6 +16,13 @@ class PlayerListener extends Component

last = state;
}

@override
void onInitialState(PlayerState state) {
super.onInitialState(state);

last ??= state;
}
}

class SadPlayerListener extends Component
Expand All @@ -33,6 +40,13 @@ class SadPlayerListener extends Component

last = state;
}

@override
void onInitialState(PlayerState state) {
super.onInitialState(state);

last ??= state;
}
}

void main() {
Expand Down Expand Up @@ -78,7 +92,7 @@ void main() {

bloc.makeSad();
await Future.microtask(() {});
expect(component.last, isNull);
expect(component.last, equals(PlayerState.alive));
},
);

Expand All @@ -96,7 +110,7 @@ void main() {

bloc.kill();
await Future.microtask(() {});
expect(component.last, isNull);
expect(component.last, equals(PlayerState.alive));
},
);

Expand Down
29 changes: 28 additions & 1 deletion packages/flame_bloc/test/src/flame_bloc_provider_test.dart
Expand Up @@ -17,6 +17,13 @@ class InventoryListener extends Component
super.onNewState(state);
lastState = state;
}

@override
void onInitialState(InventoryState state) {
super.onInitialState(state);

lastState ??= state;
}
}

void main() {
Expand Down Expand Up @@ -66,6 +73,22 @@ void main() {
expect(component.bloc, bloc);
});

testWithFlameGame(
'initial state is used to properly track last state',
(game) async {
final bloc = InventoryCubit();
late InventoryListener component;
final provider =
FlameBlocProvider<InventoryCubit, InventoryState>.value(
value: bloc,
children: [
component = InventoryListener(),
],
);
await game.ensureAdd(provider);
expect(component.lastState, equals(InventoryState.sword));
},
);
testWithFlameGame('can listen to new state changes', (game) async {
final bloc = InventoryCubit();
late InventoryListener component;
Expand All @@ -80,7 +103,11 @@ void main() {

bloc.selectBow();
await Future<void>.microtask(() {});
expect(component.lastState, equals(InventoryState.bow));

bloc.selectSword();
await Future<void>.microtask(() {});

expect(component.lastState, equals(InventoryState.sword));
});
});

Expand Down

0 comments on commit 01121c2

Please sign in to comment.