flutterflame

Navigating to a Flutter Flame game widget after leaving the game widget creates a game with no children


I have my main menu which includes a button to navigate to a Basketball game. The code for the onPressed() is:

Navigator.of(context).pushReplacement(
      MaterialPageRoute(
      builder: (context) => const BasketballGamePlay(),
    ),
  );

BasketballGamePlay code:

class BasketballGame extends FlameGame with HasCollisionDetection {
  int score = 0;
  late Timer _timer;
  int _remainingTime = 5;
  late TextComponent _scoreText;
  late TextComponent _timeText;

  @override
  void onRemove() {
    // Optional based on your game needs.
    removeAll(children);
    processLifecycleEvents();
    Flame.images.clearCache();
    Flame.assets.clearCache();
    // Any other code that you want to run when the game is removed.
  }

  @override
  Future<void> onLoad() async {
    await super.onLoad();

    add(
      BackgroundComponent(),
    );
    add(
      HoopScoreAreaComponent(),
    );
    add(
      DragInput(),
    );

    FlameAudio.audioCache.loadAll([
      Globals.basketballSound,
    ]);

    _scoreText = TextComponent(
      text: "Score: $score",
      position: Vector2(40, 40),
      anchor: Anchor.topLeft,
      textRenderer: TextPaint(
        style: TextStyle(
          color: BasicPalette.black.color,
          fontSize: 24,
        ),
      ),
    );

    add(_scoreText);

    _timeText = TextComponent(
      text: "Time: $_remainingTime seconds",
      position: Vector2(size.x - 40, 40),
      anchor: Anchor.topRight,
      textRenderer: TextPaint(
        style: TextStyle(
          color: BasicPalette.black.color,
          fontSize: 24,
        ),
      ),
    );

    add(_timeText);

    _timer = Timer(1, repeat: true, onTick: () {
      if (_remainingTime == 0) {
        pauseEngine();

        overlays.add(GameOverMenu.id);
      } else {
        _remainingTime -= 1;
      }
    });

    _timer.start;
  }

  @override
  void update(dt) {
    super.update(dt);
    _timer.update(dt);
    _scoreText.text = "Score: $score";
    _timeText.text = "Time: $_remainingTime secs";
  }

  void reset() {
    score = 0;
    _remainingTime = 5;
  }
}

When the timer in the game is done, an overlay widget appears with a button to either replay the game or a button to navigate back to the main menu. When I click the main menu button, the onRemove() function in the BasketballGamePlay gets called as I expected and the MainMenu appears. If I click the button on the MainMenu to go the game again, the BasketballGamePlay appears as a black screen with no widgets added.

The onLoad function does not fire when navigating back to the BasketballGamePlay. How should I get the BasketballGamePlay to load in all its children that are created during the onLoad function?


Solution

  • If you add your components in the game's onMount method instead of onLoad (and make sure they aren't already added), it should work with how you have set it up.

    Or you can also make sure that the GameWidget and FlameGame class is completely recreated when you press the button.