docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.
How to use use Firebase Cloud Firestore to implement multiplayer in your game.
Multiplayer games need a way to synchronize game states between players. Broadly speaking, two types of multiplayer games exist:
High tick rate. These games need to synchronize game states many times per second with low latency. These would include action games, sports games, fighting games.
Low tick rate. These games only need to synchronize game states occasionally with latency having less impact. These would include card games, strategy games, puzzle games.
This resembles the differentiation between real-time versus turn-based games, though the analogy falls short. For example, real-time strategy games run—as the name suggests—in real-time, but that doesn't correlate to a high tick rate. These games can simulate much of what happens in between player interactions on local machines. Therefore, they don't need to synchronize game states that often.
If you can choose low tick rates as a developer, you should. Low tick lowers latency requirements and server costs. Sometimes, a game requires high tick rates of synchronization. For those cases, solutions such as Firestore don't make a good fit. Pick a dedicated multiplayer server solution such as Nakama. Nakama has a Dart package.
If you expect that your game requires a low tick rate of synchronization, continue reading.
This recipe demonstrates how to use the cloud_firestore package to implement multiplayer capabilities in your game. This recipe doesn't require a server. It uses two or more clients sharing game state using Cloud Firestore.
Write your game code to allow changing the game state in response to both local events and remote events. A local event could be a player action or some game logic. A remote event could be a world update coming from the server.
To simplify this cookbook recipe, start with the card template that you'll find in the flutter/games repository. Run the following command to clone that repository:
Open the project in templates/card.
You can ignore this step and follow the recipe with your own game project. Adapt the code at appropriate places.
Cloud Firestore is a horizontally scaling, NoSQL document database in the cloud. It includes built-in live synchronization. This is perfect for our needs. It keeps the game state updated in the cloud database, so every player sees the same state.
If you want a quick, 15-minute primer on Cloud Firestore, check out the following video:
Watch on YouTube in a new tab: "What is a NoSQL Database? Learn about Cloud Firestore"
To add Firestore to your Flutter project, follow the first two steps of the Get started with Cloud Firestore guide:
The desired outcomes include:
You don't need to write any Dart code in this step. As soon as you understand the step of writing Dart code in that guide, return to this recipe.
Open lib/main.dart and import the plugins, as well as the firebase_options.dart file that was generated by flutterfire configure in the previous step.
Add the following code just above the call to runApp() in lib/main.dart:
This ensures that Firebase is initialized on game startup.
Add the Firestore instance to the app. That way, any widget can access this instance. Widgets can also react to the instance missing, if needed.
To do this with the card template, you can use the provider package (which is already installed as a dependency).
Replace the boilerplate runApp(MyApp()) with the following:
Put the provider above MyApp, not inside it. This enables you to test the app without Firebase.
:::note In case you are not working with the card template, you must either install the provider package or use your own method of accessing the FirebaseFirestore instance from various parts of your codebase. :::
Though you can talk to Firestore directly, you should write a dedicated controller class to make the code more readable and maintainable.
How you implement the controller depends on your game and on the exact design of your multiplayer experience. For the case of the card template, you could synchronize the contents of the two circular playing areas. It's not enough for a full multiplayer experience, but it's a good start.
To create a controller, copy, then paste the following code into a new file called lib/multiplayer/firestore_controller.dart.
Notice the following features of this code:
The controller's constructor takes a BoardState. This enables the controller to manipulate the local state of the game.
The controller subscribes to both local changes to update Firestore and to remote changes to update the local state and UI.
The fields _areaOneRef and _areaTwoRef are Firebase document references. They describe where the data for each area resides, and how to convert between the local Dart objects (List<PlayingCard>) and remote JSON objects (Map<String, dynamic>). The Firestore API lets us subscribe to these references with .snapshots(), and write to them with .set().
Open the file responsible for starting the play session: lib/play_session/play_session_screen.dart in the case of the card template. You instantiate the Firestore controller from this file.
Import Firebase and the controller:
Add a nullable field to the _PlaySessionScreenState class to contain a controller instance:
In the initState() method of the same class, add code that tries to read the FirebaseFirestore instance and, if successful, constructs the controller. You added the FirebaseFirestore instance to main.dart in the Initialize Firestore step.
Dispose of the controller using the dispose() method of the same class.
Run the game on two separate devices or in 2 different windows on the same device.
Watch how adding a card to an area on one device makes it appear on the other one.
Open the Firebase web console and navigate to your project's Firestore Database.
Watch how it updates the data in real time. You can even edit the data in the console and see all running clients update.
The most common issues you might encounter when testing Firebase integration include the following:
The game crashes when trying to reach Firebase.
The game doesn't communicate with Firebase on macOS.
At this point, the game has near-instant and dependable synchronization of state across clients. It lacks actual game rules: what cards can be played when, and with what results. This depends on the game itself and is left to you to try.
At this point, the shared state of the match only includes the two playing areas and the cards within them. You can save other data into _matchRef, too, like who the players are and whose turn it is. If you're unsure where to start, follow a Firestore codelab or two to familiarize yourself with the API.
At first, a single match should suffice for testing your multiplayer game with colleagues and friends. As you approach the release date, think about authentication and match-making. Thankfully, Firebase provides a built-in way to authenticate users and the Firestore database structure can handle multiple matches. Instead of a single match_1, you can populate the matches collection with as many records as needed.
An online match can start in a "waiting" state, with only the first player present. Other players can see the "waiting" matches in some kind of lobby. Once enough players join a match, it becomes "active". Once again, the exact implementation depends on the kind of online experience you want. The basics remain the same: a large collection of documents, each representing one active or potential match.
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-05-05. View source or report an issue.