Game Widget

The GameWidget is the bridge between Flutter and Flame. Since Flame games are not Flutter widgets by themselves, the GameWidget wraps a Game instance and places it into the Flutter widget tree, just like any other widget. This lets you combine a full-screen game with Flutter UI elements (navigation bars, overlays, dialogs) or embed a game as only part of your app’s layout.

class GameWidget<T extends Game>
extends StatefulWidget

The GameWidget is a Flutter widget which is used to insert a Game instance into the Flutter widget tree.

The GameWidget is sufficiently feature-rich to run as the root of your Flutter application. Thus, the simplest way to use GameWidget is like this:

void main() {
  runApp(
    GameWidget(game: MyGame()),
  );
}

At the same time, GameWidget is a regular Flutter widget, and can be inserted arbitrarily deep into the widget tree, including the possibility of having multiple GameWidgets within a single app.

The layout behavior of this widget is that it will expand to fill all available space. Thus, when used as a root widget it will make the app full-screen. Inside any other layout widget it will take as much space as possible.

In addition to hosting a Game instance, the GameWidget also provides some structural support, with the following features:

It should be noted that GameWidget does not clip the content of its canvas, which means the game can potentially draw outside of its boundaries (not always, depending on which camera is used). If this is not desired, then consider wrapping the widget in Flutter’s ClipRect.

Constructors

GameWidget({required this.game, this.textDirection, this.loadingBuilder, this.errorBuilder, this.backgroundBuilder, this.overlayBuilderMap, this.initialActiveOverlays, this.focusNode, this.autofocus = true, this.mouseCursor, this.addRepaintBoundary = true, this.behavior = HitTestBehavior.opaque, super.key})

Renders the provided game instance.

GameWidget.controlled({required this.gameFactory, this.textDirection, this.loadingBuilder, this.errorBuilder, this.backgroundBuilder, this.overlayBuilderMap, this.initialActiveOverlays, this.focusNode, this.autofocus = true, this.mouseCursor, this.addRepaintBoundary = true, this.behavior = HitTestBehavior.opaque, super.key})

A GameWidget which will create and own a Game instance, using the provided gameFactory.

This constructor can be useful when you want to put GameWidget into another widget, but would like to avoid the need to store the game’s instance yourself. For example:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20),
      child: GameWidget.controlled(
        gameFactory: MyGame.new,
      ),
    );
  }
}

Properties

game : T?

The game instance which this widget will render, if it was provided with the default constructor. Otherwise, if the GameWidget.controlled constructor was used, this will always be null.

gameFactory : GameFactory<T>?

A function that creates a Game that this widget will render.

textDirection : TextDirection?

The text direction to be used in text elements in a game.

loadingBuilder : GameLoadingWidgetBuilder?

Builder to provide a widget which will be displayed while the game is loading. By default this is an empty Container.

errorBuilder : GameErrorWidgetBuilder?

If set, errors during the game loading will be caught and this widget will be shown. If not provided, errors are propagated normally.

backgroundBuilder : WidgetBuilder?

Builder to provide a widget tree to be built between the game elements and the background color provided via Game.backgroundColor.

overlayBuilderMap : Map<String, OverlayWidgetBuilder<T>>?

A collection of widgets that can be displayed over the game’s surface. These widgets can be turned on-and-off dynamically from within the game via the Game.overlays property.

void main() {
  runApp(
    GameWidget(
      game: MyGame(),
      overlayBuilderMap: {
        'PauseMenu': (context, game) {
          return Container(
            color: const Color(0xFF000000),
            child: Text('A pause menu'),
          );
        },
      },
    ),
  );
}
initialActiveOverlays : List<String>?

The list of overlays that will be shown when the game starts (but after it was loaded).

focusNode : FocusNode?

The FocusNode to control the games focus to receive event inputs. If omitted, defaults to an internally controlled focus node.

autofocus : bool

Whether the focusNode requests focus once the game is mounted. Defaults to true.

mouseCursor : MouseCursor?

The shape of the mouse cursor when it is hovering over the game canvas. This property can be changed dynamically via Game.mouseCursor.

addRepaintBoundary : bool

Whether the game should assume the behavior of a RepaintBoundary, defaults to true.

behavior : HitTestBehavior

How the game widget behaves during hit testing.

  • HitTestBehavior.opaque (default): the game absorbs all pointer events on its surface, preventing widgets behind it from receiving them.

  • HitTestBehavior.deferToChild: the game only intercepts events at positions where a component with event callbacks (e.g. TapCallbacks) exists. Events at other positions pass through to widgets behind.

  • HitTestBehavior.translucent: the game receives events where it has event-handling components, but always allows widgets behind it to be hit-tested as well.

Hit Test Behavior

The behavior argument controls how the GameWidget participates in Flutter’s hit testing. This determines whether pointer events (taps, drags, etc.) are absorbed by the game or allowed to pass through to widgets underneath it in the widget tree.

There are three possible values from Flutter’s HitTestBehavior:

  • HitTestBehavior.opaque (default): The game absorbs all pointer events on its entire surface, preventing any widgets behind it from receiving them. This is the classic behavior where the game acts as a solid layer.

  • HitTestBehavior.deferToChild: The game only intercepts events at positions where a component with event callbacks (e.g. TapCallbacks) exists. Events at positions with no interactive components pass through to widgets behind the GameWidget. This is useful when layering a game on top of Flutter UI and you want the underlying widgets to remain interactive in areas the game doesn’t need to handle.

  • HitTestBehavior.translucent: The game receives events where it has event-handling components, but always allows widgets behind it to be hit-tested as well. Both the game and the widgets behind it can receive the same event.

Allowing taps to pass through

A common use case is placing a GameWidget on top of other Flutter widgets in a Stack. By default, the game will block all interaction with the widgets underneath. To let taps pass through to those widgets, set behavior to HitTestBehavior.deferToChild:

Widget build(BuildContext context) {
  return Stack(
    children: [
      // Flutter widgets underneath
      Center(
        child: ElevatedButton(
          onPressed: () => print('Button tapped!'),
          child: const Text('Tap me'),
        ),
      ),
      // Game on top, letting taps pass through
      Positioned.fill(
        child: GameWidget(
          game: MyGame(),
          behavior: HitTestBehavior.deferToChild,
        ),
      ),
    ],
  );
}

In this setup, tapping an area with no interactive game components will reach the ElevatedButton behind the game. Tapping a game component that uses TapCallbacks will be handled by the game instead.

Note

When using deferToChild or translucent, FlameGame determines whether a position has an interactive component by traversing the component tree via componentsAtPoint. Games that directly extend the low-level Game class report a hit on their entire surface by default; override containsEventHandlerAt to customize this.