Skip to content

Commit

Permalink
fix: Hardcode initCurrentGame lifecycle state as resumed (#2775)
Browse files Browse the repository at this point in the history
When the init game method is called straight after the app has launched, the lifecycle state from WidgetsBinding.instance.lifecycleState often returns disposed or inactive which causes the engine to stay paused even though the app is foregrounded.
This PR fixes the lifecycle state as resumed when the init method is called.
  • Loading branch information
adil192 committed Oct 1, 2023
1 parent d33bb4a commit 0cd5037
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 19 deletions.
19 changes: 15 additions & 4 deletions packages/flame/lib/src/game/game_widget/game_widget.dart
Expand Up @@ -253,13 +253,24 @@ class GameWidgetState<T extends Game> extends State<GameWidget<T>> {
} else {
currentGame = widget.game!;
}
currentGame.addGameStateListener(_onGameStateChange);
currentGame.lifecycleStateChange(
WidgetsBinding.instance.lifecycleState ?? AppLifecycleState.resumed,
);
initGameStateListener(currentGame, _onGameStateChange);
_loaderFuture = null;
}

/// Visible for testing for
/// https://github.com/flame-engine/flame/issues/2771.
@visibleForTesting
static void initGameStateListener(
Game currentGame,
void Function() onGameStateChange,
) {
currentGame.addGameStateListener(onGameStateChange);

// See https://github.com/flame-engine/flame/issues/2771
// for why we aren't using [WidgetsBinding.instance.lifecycleState].
currentGame.lifecycleStateChange(AppLifecycleState.resumed);
}

/// [disposeCurrentGame] is called by two flutter events - `didUpdateWidget`
/// and `dispose`. When the parameter [callGameOnDispose] is true, the
/// `currentGame`'s `onDispose` method will be called; otherwise, it will not.
Expand Down
46 changes: 31 additions & 15 deletions packages/flame/test/game/flame_game_test.dart
Expand Up @@ -720,21 +720,37 @@ void main() {
expect(game.paused, isFalse);
});

testWidgets(
'game is not paused on start',
(tester) async {
final game = FlameGame();

await tester.pumpWidget(
GameWidget(game: game),
);

await game.toBeLoaded();
await tester.pump();

expect(game.paused, isFalse);
},
);
for (final startingLifecycleState in AppLifecycleState.values) {
testWidgets(
'game is not paused on start when initially $startingLifecycleState',
(tester) async {
WidgetsBinding.instance.handleAppLifecycleStateChanged(
startingLifecycleState,
);
addTearDown(() {
// Don't use [WidgetsBinding.instance.resetLifecycleState()]
// because it sets the lifecycle to null which prevents
// [game.onLoad] from running in other tests.
WidgetsBinding.instance.handleAppLifecycleStateChanged(
AppLifecycleState.resumed,
);
});
expect(
WidgetsBinding.instance.lifecycleState,
startingLifecycleState,
);

final game = FlameGame();

final gameWidget = GameWidget(game: game);
await tester.pumpWidget(gameWidget);

GameWidgetState.initGameStateListener(game, () {});

expect(game.paused, isFalse);
},
);
}

testWidgets(
'game is paused when app is backgrounded',
Expand Down

0 comments on commit 0cd5037

Please sign in to comment.