diff --git a/doc/flame/game.md b/doc/flame/game.md index baeab55a090..cdfdafd6b0d 100644 --- a/doc/flame/game.md +++ b/doc/flame/game.md @@ -94,12 +94,13 @@ Once the `GameWidget` is removed from the tree, `onRemove` is called, just like component is removed from the component tree. ```{note} -The `onRemove` can be used to clean up potential memory leaks by doing the following: +The `onRemove` can be used to clean up potential memory leaks such as the following: ``` ```dart @override void onRemove() { + // Optional based on your game needs. removeAll(children); processLifecycleEvents(); Flame.images.clearCache(); diff --git a/packages/flame/lib/src/game/game.dart b/packages/flame/lib/src/game/game.dart index 53f2e2e88bb..a028943b9ce 100644 --- a/packages/flame/lib/src/game/game.dart +++ b/packages/flame/lib/src/game/game.dart @@ -190,7 +190,10 @@ abstract mixin class Game { ); } _gameRenderBox = gameRenderBox; - onAttach(); + if (!_isInternalRefresh) { + onAttach(); + } + _isInternalRefresh = false; } /// Called when the game has been attached. This can be overridden @@ -202,10 +205,10 @@ abstract mixin class Game { /// /// Should not be called manually. void detach() { - onRemove(); + if (!_isInternalRefresh) { + onDetach(); + } _gameRenderBox = null; - - onDetach(); } /// Called when the game is about to be removed from the Flutter widget tree, @@ -358,11 +361,17 @@ abstract mixin class Game { gameStateListeners.remove(callback); } + bool _isInternalRefresh = false; + /// When a Game is attached to a `GameWidget`, this method will force that /// widget to be rebuilt. This can be used when updating any property which is /// implemented within the Flutter tree. + /// + /// When [isInternalRefresh] is passed as false it will trigger the `onAttach` + /// and `onDetach` events; otherwise, those events will not be called. @internal - void refreshWidget() { + void refreshWidget({bool isInternalRefresh = true}) { + _isInternalRefresh = isInternalRefresh; gameStateListeners.forEach((callback) => callback()); } } diff --git a/packages/flame/lib/src/game/game_widget/game_widget.dart b/packages/flame/lib/src/game/game_widget/game_widget.dart index fdf734c3bac..f21dade9a15 100644 --- a/packages/flame/lib/src/game/game_widget/game_widget.dart +++ b/packages/flame/lib/src/game/game_widget/game_widget.dart @@ -259,6 +259,7 @@ class GameWidgetState extends State> { /// `currentGame`'s `onDispose` method will be called; otherwise, it will not. void disposeCurrentGame({bool callGameOnDispose = false}) { currentGame.removeGameStateListener(_onGameStateChange); + currentGame.onRemove(); if (callGameOnDispose) { currentGame.onDispose(); } diff --git a/packages/flame/lib/src/game/overlay_manager.dart b/packages/flame/lib/src/game/overlay_manager.dart index b53fe6c6e6c..e7f723a17b4 100644 --- a/packages/flame/lib/src/game/overlay_manager.dart +++ b/packages/flame/lib/src/game/overlay_manager.dart @@ -25,14 +25,14 @@ class OverlayManager { /// Clears all active overlays. void clear() { _activeOverlays.clear(); - _game.refreshWidget(); + _game.refreshWidget(isInternalRefresh: false); } /// Marks the [overlayName] to be rendered. bool add(String overlayName) { final setChanged = _addImpl(overlayName); if (setChanged) { - _game.refreshWidget(); + _game.refreshWidget(isInternalRefresh: false); } return setChanged; } @@ -42,7 +42,7 @@ class OverlayManager { final initialCount = _activeOverlays.length; overlayNames.forEach(_addImpl); if (initialCount != _activeOverlays.length) { - _game.refreshWidget(); + _game.refreshWidget(isInternalRefresh: false); } } @@ -67,7 +67,7 @@ class OverlayManager { bool remove(String overlayName) { final hasRemoved = _activeOverlays.remove(overlayName); if (hasRemoved) { - _game.refreshWidget(); + _game.refreshWidget(isInternalRefresh: false); } return hasRemoved; } @@ -77,7 +77,7 @@ class OverlayManager { final initialCount = _activeOverlays.length; overlayNames.forEach(_activeOverlays.remove); if (_activeOverlays.length != initialCount) { - _game.refreshWidget(); + _game.refreshWidget(isInternalRefresh: false); } }