CINXE.COM
Add multiplayer support using Firestore | Flutter
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Add multiplayer support using Firestore | Flutter</title><link rel="icon" href="/assets/images/branding/flutter/icon/64.png" eleventy:ignore><link rel="apple-touch-icon" href="/assets/images/branding/flutter/logo/flutter-logomark-320px.png" eleventy:ignore><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#ffffff"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><meta name="google-site-verification" content="HFqxhSbf9YA_0rBglNLzDiWnrHiK_w4cqDh2YD2GEY4"><script>!function(e,t,a,n){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var g=t.getElementsByTagName(a)[0],m=t.createElement(a);m.async=!0,m.src="https://www.googletagmanager.com/gtm.js?id=GTM-ND4LWWZ",g.parentNode.insertBefore(m,g)}(window,document,"script","dataLayer")</script><script>!function(e,a,t,n,c,o,s){e.GoogleAnalyticsObject=c,e[c]=e[c]||function(){(e[c].q=e[c].q||[]).push(arguments)},e[c].l=1*new Date,o=a.createElement(t),s=a.getElementsByTagName(t)[0],o.async=1,o.src="//www.google-analytics.com/analytics.js",s.parentNode.insertBefore(o,s)}(window,document,"script",0,"ga"),ga("create","UA-67589403-1","auto"),ga("send","pageview")</script><meta name="description" content="How to use use Firebase Cloud Firestore to implement multiplayer in your game."><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@flutterdev"><meta property="og:title" content="Add multiplayer support using Firestore"><meta property="og:url" content="https://docs.flutter.dev/cookbook/games/firestore-multiplayer"><meta property="og:description" content="How to use use Firebase Cloud Firestore to implement multiplayer in your game."><meta property="og:image" content="https://docs.flutter.dev/assets/images/flutter-logo-sharing.png" eleventy:ignore><link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" rel="stylesheet"><script>window.__CALLBACKS=[]</script><link rel="stylesheet" href="/assets/css/main.css?v=4"></head><body><section id="cookie-notice"><div class="container"><p>docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic. <a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">Learn more</a>.</p><button id="cookie-consent" class="filled-button">OK, got it</button></div></section><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-ND4LWWZ" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><div id="site-banner" role="alert"><p>Flutter 3.29 is here with a bouquet of performance and fidelity improvements for your apps! <a href="https://medium.com/flutter/whats-new-in-flutter-3-29-f90c380c2317">Learn more</a><br></p></div><header class="site-header"><a href="#document-title" id="skip-to-main" class="filled-button" tabindex="1">Skip to main content</a><nav class="navbar"><div id="site-switcher" class="dropdown"><button class="dropdown-button site-wordmark" aria-expanded="false" aria-controls="site-switcher-menu" aria-label="Switch between Flutter and Dart sites"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">Docs</span> <span class="material-symbols" aria-hidden="true">unfold_more</span></button><div class="dropdown-content" id="site-switcher-menu"><nav class="dropdown-menu" role="menu"><ul><li role="presentation"><a href="https://flutter.dev" class="site-wordmark" role="menuitem" title="Flutter homepage" aria-label="Go to the Flutter homepage"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span></a></li><li role="presentation"><a href="https://docs.flutter.dev" class="site-wordmark current-site" role="menuitem" aria-current="true" title="Flutter docs homepage" aria-label="Go to the Flutter docs homepage"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">Docs</span></a></li><li role="presentation"><a href="https://api.flutter.dev" class="site-wordmark" role="menuitem" title="Flutter API reference" aria-label="Go to the Flutter API reference"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">API</span></a></li><li aria-hidden="true" class="dropdown-divider" role="separator"></li><li role="presentation"><a href="https://dart.dev" class="site-wordmark" role="menuitem" title="Dart homepage" aria-label="Go to the Dart homepage"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>Dart</span></a></li><li role="presentation"><a href="https://dartpad.dev" class="site-wordmark" role="menuitem" title="DartPad playground" aria-label="Go to the DartPad playground"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>DartPad</span></a></li><li role="presentation"><a href="https://pub.dev" class="site-wordmark" role="menuitem" title="pub.dev homepage" aria-label="Go to the pub.dev homepage"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>pub.dev</span></a></li></ul></nav></div></div><div class="navbar-contents"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="https://flutter.dev">Homepage</a></li><li class="nav-item"><a class="nav-link" href="https://flutter.dev/community">Community</a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev">Packages</a></li><li class="nav-item"><a class="nav-link" href="https://api.flutter.dev">API reference</a></li></ul><form action="/search/" class="site-header__search"><input class="site-header__searchfield search-field" type="search" name="q" id="q" autocomplete="off" placeholder="Search" aria-label="Search"></form><a href="/search" id="fallback-search-button" class="icon-button" aria-label="Navigate to the docs search page." title="Navigate to the docs search page."><span class="material-symbols" aria-hidden="true">search</span> </a><a id="call-to-action" class="filled-button" href="/get-started/install/">Get started</a> <button id="menu-toggle" class="icon-button" type="button" aria-controls="sidenav" aria-label="Toggle navigation" title="Toggle navigation"><span class="material-symbols" aria-hidden="true">menu</span></button></div></nav></header><div id="site-below-header"><div id="site-main-row"><div id="sidenav" class="site-sidebar"><nav><ul><li class="nav-header">Get started</li><li class="nav-item"><a class="nav-link" href="/get-started/install"><div><span>Set up Flutter</span></div></a></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-3" role="button" aria-expanded="false" aria-controls="sidenav-3"><span>Learn Flutter</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-3"><li class="nav-item"><a class="nav-link" href="/get-started/learn-flutter"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/codelab"><div><span>Write your first app</span></div></a></li><li class="nav-item"><button class="nav-link collapsible" data-toggle="collapse" data-target="#sidenav-3-3" role="button" aria-expanded="true" aria-controls="sidenav-3-3"><span>Learn the fundamentals</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-3-3"><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/dart"><div><span>Intro to Dart</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/widgets"><div><span>Widgets</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/layout"><div><span>Layout</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/state-management"><div><span>State management</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/user-input"><div><span>Handling user input</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/networking"><div><span>Networking and data</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/local-caching"><div><span>Local data and caching</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-3-4" role="button" aria-expanded="false" aria-controls="sidenav-3-4"><span>From another platform?</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-3-4"><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/android-devs"><div><span>Flutter for Android devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/compose-devs"><div><span>Flutter for Jetpack Compose devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/swiftui-devs"><div><span>Flutter for SwiftUI devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/uikit-devs"><div><span>Flutter for UIKit devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/react-native-devs"><div><span>Flutter for React Native devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/web-devs"><div><span>Flutter for web devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/xamarin-forms-devs"><div><span>Flutter for Xamarin.Forms devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/declarative"><div><span>Introduction to declarative UI</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/dart-swift-concurrency"><div><span>Flutter versus Swift concurrency</span></div></a></li></ul></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/codelabs"><div><span>Codelabs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook"><div><span>Cookbook</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://github.com/flutter/samples" target="_blank" rel="noopener"><div><span>Demos and samples</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-4" role="button" aria-expanded="false" aria-controls="sidenav-4"><span>Stay up to date</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-4"><li class="nav-item"><a class="nav-link" href="/release/upgrade"><div><span>Upgrade</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/archive"><div><span>SDK archive</span></div></a></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/release/whats-new"><div><span>What's new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/release-notes"><div><span>Release notes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/breaking-changes"><div><span>Breaking changes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/compatibility-policy"><div><span>Compatibility policy</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-5" role="button" aria-expanded="true" aria-controls="sidenav-5"><span>App solutions</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-5"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-1" role="button" aria-expanded="false" aria-controls="sidenav-5-1"><span>AI</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-1"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-1-1" role="button" aria-expanded="false" aria-controls="sidenav-5-1-1"><span>AI Toolkit guide</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-1-1"><li class="nav-item"><a class="nav-link" href="/ai-toolkit"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/user-experience"><div><span>User experience</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/feature-integration"><div><span>Feature integration</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/custom-llm-providers"><div><span>Custom LLM providers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/chat-client-sample"><div><span>Chat client sample</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="https://www.youtube.com/watch?v=1AuzJEiHjO4" target="_blank" rel="noopener"><div><span>Build with Google AI Dart SDK (video)</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-2" role="button" aria-expanded="false" aria-controls="sidenav-5-2"><span>Firebase & Firestore</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-2"><li class="nav-item"><a class="nav-link" href="/data-and-backend/firebase"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/flutter" target="_blank" rel="noopener"><div><span>Discover Firebase for Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://www.youtube.com/watch?v=wUSkeTaBonA" target="_blank" rel="noopener"><div><span>Get to know Firebase for Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps" target="_blank" rel="noopener"><div><span>Add a user authentication flow to a Flutter app using FirebaseUI</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-get-to-know-web" target="_blank" rel="noopener"><div><span>Get to know Firebase for web</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-5-3" role="button" aria-expanded="true" aria-controls="sidenav-5-3"><span>Games</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-5-3"><li class="nav-item"><a class="nav-link" href="/resources/games-toolkit"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/games/achievements-leaderboard"><div><span>Add achievements and leaderboards</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/build-leaderboards-with-firestore#0" target="_blank" rel="noopener"><div><span>Build leaderboards with Firestore</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/google-mobile-ads"><div><span>Add advertising</span></div></a></li><li class="nav-item"><a class="nav-link active" href="/cookbook/games/firestore-multiplayer"><div><span>Add multiplayer support</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases" target="_blank" rel="noopener"><div><span>Add in-app purchases</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps" target="_blank" rel="noopener"><div><span>Add user authentication</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/crashlytics/get-started?platform=flutter" target="_blank" rel="noopener"><div><span>Debug using Crashlytics</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-flame-brick-breaker" target="_blank" rel="noopener"><div><span>Intro to Flame with Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4" role="button" aria-expanded="false" aria-controls="sidenav-5-4"><span>Monetization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-1" role="button" aria-expanded="false" aria-controls="sidenav-5-4-1"><span>Advertising</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-1"><li class="nav-item"><a class="nav-link" href="/resources/ads-overview"><div><span>Ads overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/google-mobile-ads"><div><span>Add advertising</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/admob-ads-in-flutter" target="_blank" rel="noopener"><div><span>Add AdMob ads to your Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter" target="_blank" rel="noopener"><div><span>Add an AdMob banner and native inline ads</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://developers.google.com/admob/flutter/mediation" target="_blank" rel="noopener"><div><span>Google AdMob mediation</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/interactive_media_ads" target="_blank" rel="noopener"><div><span>Interactive Media Ads SDK</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-2" role="button" aria-expanded="false" aria-controls="sidenav-5-4-2"><span>In-app purchases</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-2"><li class="nav-item"><a class="nav-link" href="/resources/in-app-purchases-overview"><div><span>In-app purchases overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases" target="_blank" rel="noopener"><div><span>Add in-app purchases</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-3" role="button" aria-expanded="false" aria-controls="sidenav-5-4-3"><span>Payments</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-3"><li class="nav-item"><a class="nav-link" href="/resources/payments-overview"><div><span>Payments overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/pay" target="_blank" rel="noopener"><div><span>Google pay package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-5" role="button" aria-expanded="false" aria-controls="sidenav-5-5"><span>Maps</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-5"><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/google-maps-in-flutter" target="_blank" rel="noopener"><div><span>Add Google maps to a Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://developers.google.com/maps/flutter-package" target="_blank" rel="noopener"><div><span>Google Maps package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-6" role="button" aria-expanded="false" aria-controls="sidenav-5-6"><span>News</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-6"><li class="nav-item"><a class="nav-link" href="/resources/news-toolkit"><div><span>Build a news app</span></div></a></li></ul></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-header">User interface</li><li class="nav-item"><a class="nav-link" href="/ui"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/widgets"><div><span>Widget catalog</span></div></a></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-10" role="button" aria-expanded="false" aria-controls="sidenav-10"><span>Layout</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10"><li class="nav-item"><a class="nav-link" href="/ui/layout"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/tutorial"><div><span>Build a layout</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-10-3" role="button" aria-expanded="false" aria-controls="sidenav-10-3"><span>Lists & grids</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10-3"><li class="nav-item"><a class="nav-link" href="/cookbook/lists/basic-list"><div><span>Create and use lists</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/horizontal-list"><div><span>Create a horizontal list</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/grid-lists"><div><span>Create a grid view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/mixed-list"><div><span>Create lists with different types of items</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/spaced-items"><div><span>Create lists with spaced items</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/long-lists"><div><span>Work with long lists</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-10-4" role="button" aria-expanded="false" aria-controls="sidenav-10-4"><span>Scrolling</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10-4"><li class="nav-item"><a class="nav-link" href="/ui/layout/scrolling"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/scrolling/slivers"><div><span>Use slivers to achieve fancy scrolling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/floating-app-bar"><div><span>Place a floating app bar above a list</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/parallax-scrolling"><div><span>Create a scrolling parallax effect</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-11" role="button" aria-expanded="false" aria-controls="sidenav-11"><span>Adaptive & responsive design</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-11"><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/general"><div><span>General approach</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/safearea-mediaquery"><div><span>SafeArea & MediaQuery</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/large-screens"><div><span>Large screens & foldables</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/input"><div><span>User input & accessibility</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/capabilities"><div><span>Capabilities & policies</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/platform-adaptations"><div><span>Automatic platform adaptations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/best-practices"><div><span>Best practices</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/more-info"><div><span>Additional resources</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-12" role="button" aria-expanded="false" aria-controls="sidenav-12"><span>Design & theming</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12"><li class="nav-item"><a class="nav-link" href="/cookbook/design/themes"><div><span>Share styles with themes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/design/material"><div><span>Material design</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/breaking-changes/material-3-migration"><div><span>Migrate to Material 3</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-12-4" role="button" aria-expanded="false" aria-controls="sidenav-12-4"><span>Text</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12-4"><li class="nav-item"><a class="nav-link" href="/ui/design/text/typography"><div><span>Fonts & typography</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/fonts"><div><span>Use a custom font</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/package-fonts"><div><span>Export fonts from a package</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/google_fonts" target="_blank" rel="noopener"><div><span>Google Fonts package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-12-5" role="button" aria-expanded="false" aria-controls="sidenav-12-5"><span>Custom graphics</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12-5"><li class="nav-item"><a class="nav-link" href="/ui/design/graphics/fragment-shaders"><div><span>Use custom fragment shaders</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-13" role="button" aria-expanded="false" aria-controls="sidenav-13"><span>Interactivity</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13"><li class="nav-item"><a class="nav-link" href="/ui/interactivity"><div><span>Add interactivity to your app</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-13-2" role="button" aria-expanded="false" aria-controls="sidenav-13-2"><span>Gestures</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13-2"><li class="nav-item"><a class="nav-link" href="/ui/interactivity/gestures"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/handling-taps"><div><span>Handle taps</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/gestures/drag-outside"><div><span>Drag an object outside an app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/drag-a-widget"><div><span>Drag a UI element within an app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/ripples"><div><span>Add Material touch ripples</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/dismissible"><div><span>Implement swipe to dismiss</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-13-3" role="button" aria-expanded="false" aria-controls="sidenav-13-3"><span>Input & forms</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13-3"><li class="nav-item"><a class="nav-link" href="/cookbook/forms/text-input"><div><span>Create and style a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/retrieve-input"><div><span>Retrieve the value of a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/text-field-changes"><div><span>Handle changes to a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/focus"><div><span>Manage focus in text fields</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/validation"><div><span>Build a form with validation</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/snackbars"><div><span>Display a snackbar</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/actions-and-shortcuts"><div><span>Implement actions & shortcuts</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/focus"><div><span>Manage keyboard focus</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-14" role="button" aria-expanded="false" aria-controls="sidenav-14"><span>Assets & media</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-14"><li class="nav-item"><a class="nav-link" href="/ui/assets/assets-and-images"><div><span>Add assets and images</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/images/network-image"><div><span>Display images from the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/images/fading-in-images"><div><span>Fade in images with a placeholder</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/play-video"><div><span>Play and pause a video</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/assets/asset-transformation"><div><span>Transform assets at build time</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-15" role="button" aria-expanded="false" aria-controls="sidenav-15"><span>Navigation & routing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-15"><li class="nav-item"><a class="nav-link" href="/ui/navigation"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/tabs"><div><span>Add tabs to your app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/navigation-basics"><div><span>Navigate to a new screen and back</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/passing-data"><div><span>Send data to a new screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/returning-data"><div><span>Return data from a screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/drawer"><div><span>Add a drawer to a screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/navigation/deep-linking"><div><span>Set up deep linking</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/set-up-app-links"><div><span>Set up app links for Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/set-up-universal-links"><div><span>Set up universal links for iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/navigation/url-strategies"><div><span>Configure web URL strategies</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-16" role="button" aria-expanded="false" aria-controls="sidenav-16"><span>Animations & transitions</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-16"><li class="nav-item"><a class="nav-link" href="/ui/animations"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/tutorial"><div><span>Tutorial</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/implicit-animations"><div><span>Implicit animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/animated-container"><div><span>Animate the properties of a container</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/opacity-animation"><div><span>Fade a widget in and out</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/hero-animations"><div><span>Hero animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/page-route-animation"><div><span>Animate a page route transition</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/physics-simulation"><div><span>Animate using a physics simulation</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/staggered-animations"><div><span>Staggered animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/staggered-menu-animation"><div><span>Create a staggered menu animation</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/overview"><div><span>API overview</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-17" role="button" aria-expanded="false" aria-controls="sidenav-17"><span>Accessibility & internationalization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-17"><li class="nav-item"><a class="nav-link" href="/ui/accessibility-and-internationalization/accessibility"><div><span>Accessibility</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/accessibility-and-internationalization/internationalization"><div><span>Internationalization</span></div></a></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-header">Beyond UI</li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-20" role="button" aria-expanded="false" aria-controls="sidenav-20"><span>Data & backend</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-1" role="button" aria-expanded="false" aria-controls="sidenav-20-1"><span>State management</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-1"><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/intro"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/declarative"><div><span>Think declaratively</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/ephemeral-vs-app"><div><span>Ephemeral vs app state</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/simple"><div><span>Simple app state management</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/options"><div><span>Options</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-2" role="button" aria-expanded="false" aria-controls="sidenav-20-2"><span>Networking & http</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-2"><li class="nav-item"><a class="nav-link" href="/data-and-backend/networking"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/fetch-data"><div><span>Fetch data from the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/authenticated-requests"><div><span>Make authenticated requests</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/send-data"><div><span>Send data to the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/update-data"><div><span>Update data over the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/delete-data"><div><span>Delete data on the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/web-sockets"><div><span>Communicate with WebSockets</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-3" role="button" aria-expanded="false" aria-controls="sidenav-20-3"><span>Serialization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-3"><li class="nav-item"><a class="nav-link" href="/data-and-backend/serialization/json"><div><span>JSON serialization</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/background-parsing"><div><span>Parse JSON in the background</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-4" role="button" aria-expanded="false" aria-controls="sidenav-20-4"><span>Persistence</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-4"><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/key-value"><div><span>Store key-value data on disk</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/reading-writing-files"><div><span>Read and write files</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/sqlite"><div><span>Persist data with SQLite</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-5" role="button" aria-expanded="false" aria-controls="sidenav-20-5"><span>Firebase</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-5"><li class="nav-item"><a class="nav-link" href="/data-and-backend/firebase"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/flutter/setup" target="_blank" rel="noopener"><div><span>Add Firebase to your Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/google-apis"><div><span>Google APIs</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-21" role="button" aria-expanded="false" aria-controls="sidenav-21"><span>App architecture</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-21"><li class="nav-item"><a class="nav-link" href="/app-architecture"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/concepts"><div><span>Architecture concepts</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/guide"><div><span>Guide to app architecture</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-21-4" role="button" aria-expanded="false" aria-controls="sidenav-21-4"><span>Architecture case study</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-21-4"><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/ui-layer"><div><span>UI layer</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/data-layer"><div><span>Data layer</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/dependency-injection"><div><span>Dependency injection</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/testing"><div><span>Testing each layer</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/app-architecture/recommendations"><div><span>Recommendations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/design-patterns"><div><span>Design patterns</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-22" role="button" aria-expanded="false" aria-controls="sidenav-22"><span>Platform integration</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22"><li class="nav-item"><a class="nav-link" href="/reference/supported-platforms"><div><span>Supported platforms</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/desktop"><div><span>Build desktop apps with Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/platform-channels"><div><span>Write platform-specific code</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-4" role="button" aria-expanded="false" aria-controls="sidenav-22-4"><span>Android</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-4"><li class="nav-item"><a class="nav-link" href="/platform-integration/android/install-android"><div><span>Add Android as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/splash-screen"><div><span>Add a splash screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/predictive-back"><div><span>Add predictive back</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/platform-views"><div><span>Host a native Android view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/call-jetpack-apis"><div><span>Calling JetPack APIs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/compose-activity"><div><span>Launch a Jetpack Compose activity</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/restore-state-android"><div><span>Restore state on Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/chromeos"><div><span>Target ChromeOS with Android</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-5" role="button" aria-expanded="false" aria-controls="sidenav-22-5"><span>iOS</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-5"><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/install-ios"><div><span>Add iOS as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-latest"><div><span>Flutter on latest iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/apple-frameworks"><div><span>Leverage Apple's system libraries</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/launch-screen"><div><span>Add a launch screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-app-clip"><div><span>Add iOS App Clip support</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/app-extensions"><div><span>Add iOS app extensions</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/platform-views"><div><span>Host a native iOS view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-debugging"><div><span>Enable debugging on iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/restore-state-ios"><div><span>Restore state on iOS</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-6" role="button" aria-expanded="false" aria-controls="sidenav-22-6"><span>Linux</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-6"><li class="nav-item"><a class="nav-link" href="/platform-integration/linux/install-linux"><div><span>Add Linux as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/linux/building"><div><span>Build a Linux app</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-7" role="button" aria-expanded="false" aria-controls="sidenav-22-7"><span>macOS</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-7"><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/install-macos"><div><span>Add macOS as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/building"><div><span>Build a macOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/platform-views"><div><span>Host a native macOS view</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-8" role="button" aria-expanded="false" aria-controls="sidenav-22-8"><span>Web</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-8"><li class="nav-item"><a class="nav-link" href="/platform-integration/web"><div><span>Web support in Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/install-web"><div><span>Add web as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/building"><div><span>Build a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/wasm"><div><span>Compile to WebAssembly</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/initialization"><div><span>Customize app initialization</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/embedding-flutter-web"><div><span>Add Flutter to any web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/web-content-in-flutter"><div><span>Web content in Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/renderers"><div><span>Web renderers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/web-images"><div><span>Display images on the web</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/faq"><div><span>Web FAQ</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-9" role="button" aria-expanded="false" aria-controls="sidenav-22-9"><span>Windows</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-9"><li class="nav-item"><a class="nav-link" href="/platform-integration/windows/install-windows"><div><span>Add Windows as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/windows/building"><div><span>Build a Windows app</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-23" role="button" aria-expanded="false" aria-controls="sidenav-23"><span>Packages & plugins</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-23"><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/using-packages"><div><span>Use packages & plugins</span></div></a></li><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/developing-packages"><div><span>Develop packages & plugins</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-23-3" role="button" aria-expanded="false" aria-controls="sidenav-23-3"><span>Swift Package Manager</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-23-3"><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/swift-package-manager/for-app-developers"><div><span>For app developers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/swift-package-manager/for-plugin-authors"><div><span>For plugin authors</span></div></a></li></ul></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/favorites"><div><span>Flutter Favorites</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/flutter" target="_blank" rel="noopener"><div><span>Package repository</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-24" role="button" aria-expanded="false" aria-controls="sidenav-24"><span>Testing & debugging</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24"><li class="nav-header">Testing</li><li class="nav-item"><a class="nav-link" href="/testing/overview"><div><span>Overview</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-3" role="button" aria-expanded="false" aria-controls="sidenav-24-3"><span>Unit testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-3"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/unit/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/unit/mocking"><div><span>Mock dependencies</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-4" role="button" aria-expanded="false" aria-controls="sidenav-24-4"><span>Widget testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-4"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/finders"><div><span>Find widgets</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/scrolling"><div><span>Simulate scrolling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/tap-drag"><div><span>Simulate user interaction</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-5" role="button" aria-expanded="false" aria-controls="sidenav-24-5"><span>Integration testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-5"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/integration/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/integration-tests"><div><span>Write and run an integration test</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/integration/profiling"><div><span>Profile an integration test</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/testing/testing-plugins"><div><span>Test a plugin</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/plugins-in-tests"><div><span>Handle plugin code in tests</span></div></a></li><li class="nav-header">Debugging</li><li class="nav-item"><a class="nav-link" href="/testing/debugging"><div><span>Debugging tools</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/code-debugging"><div><span>Debug your app programmatically</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/native-debugging"><div><span>Use a native language debugger</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/common-errors"><div><span>Common Flutter errors</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/errors"><div><span>Handle errors</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/maintenance/error-reporting"><div><span>Report errors to a service</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-25" role="button" aria-expanded="false" aria-controls="sidenav-25"><span>Performance & optimization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-25"><li class="nav-item"><a class="nav-link" href="/perf"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/impeller"><div><span>Impeller</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/best-practices"><div><span>Performance best practices</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/app-size"><div><span>App size</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/deferred-components"><div><span>Deferred components</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/rendering-performance"><div><span>Rendering performance</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/ui-performance"><div><span>Performance profiling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/web-performance"><div><span>Performance profiling for web</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/metrics"><div><span>Performance metrics</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/isolates"><div><span>Concurrency and isolates</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/faq"><div><span>Performance FAQ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/appendix"><div><span>Appendix</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-26" role="button" aria-expanded="false" aria-controls="sidenav-26"><span>Deployment</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-26"><li class="nav-item"><a class="nav-link" href="/deployment/obfuscate"><div><span>Obfuscate Dart code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/flavors"><div><span>Create app flavors for Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/flavors-ios"><div><span>Create app flavors for iOS and macOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/android"><div><span>Build and release an Android app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/ios"><div><span>Build and release an iOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/macos"><div><span>Build and release a macOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/linux"><div><span>Build and release a Linux app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/windows"><div><span>Build and release a Windows app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/web"><div><span>Build and release a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/cd"><div><span>Set up continuous deployment</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-27" role="button" aria-expanded="false" aria-controls="sidenav-27"><span>Add to an existing app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27"><li class="nav-item"><a class="nav-link" href="/add-to-app"><div><span>Introduction</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-27-2" role="button" aria-expanded="false" aria-controls="sidenav-27-2"><span>Add to an Android app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27-2"><li class="nav-item"><a class="nav-link" href="/add-to-app/android/project-setup"><div><span>Set up Android project</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-screen"><div><span>Add a single Flutter screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-fragment"><div><span>Add a Flutter Fragment</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-view"><div><span>Add a Flutter View</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/plugin-setup"><div><span>Use a Flutter plugin</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-27-3" role="button" aria-expanded="false" aria-controls="sidenav-27-3"><span>Add to an iOS app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27-3"><li class="nav-item"><a class="nav-link" href="/add-to-app/ios/project-setup"><div><span>Set up iOS project</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/ios/add-flutter-screen"><div><span>Add a single Flutter screen</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/embedding-flutter-web"><div><span>Add to a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/debugging"><div><span>Debug embedded Flutter module</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/multiple-flutters"><div><span>Add multiple Flutter instances</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/performance"><div><span>Loading sequence and performance</span></div></a></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-29" role="button" aria-expanded="false" aria-controls="sidenav-29"><span>Tools & editors</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-29"><li class="nav-item"><a class="nav-link" href="/tools/android-studio"><div><span>Android Studio & IntelliJ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/vs-code"><div><span>Visual Studio Code</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-29-3" role="button" aria-expanded="false" aria-controls="sidenav-29-3"><span>DevTools</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-29-3"><li class="nav-item"><a class="nav-link" href="/tools/devtools"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/android-studio"><div><span>Run from Android Studio & IntelliJ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/vscode"><div><span>Run from VS Code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/cli"><div><span>Run from command line</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/inspector"><div><span>Flutter inspector</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/legacy-inspector"><div><span>Legacy Flutter inspector</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/performance"><div><span>Performance view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/cpu-profiler"><div><span>CPU Profiler view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/memory"><div><span>Memory view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/console"><div><span>Debug console view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/network"><div><span>Network view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/debugger"><div><span>Debugger</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/logging"><div><span>Logging view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/app-size"><div><span>App size tool</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/extensions"><div><span>DevTools extensions</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/deep-links"><div><span>Validate deep links</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/release-notes"><div><span>Release notes</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/tools/sdk"><div><span>SDK overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/pubspec"><div><span>Flutter's pubspec options</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/flutter-fix"><div><span>Automated fixes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/formatting"><div><span>Code formatting</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-30" role="button" aria-expanded="false" aria-controls="sidenav-30"><span>Flutter concepts</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-30"><li class="nav-item"><a class="nav-link" href="/resources/architectural-overview"><div><span>Architectural overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/inside-flutter"><div><span>Inside Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/constraints"><div><span>Understanding constraints</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/build-modes"><div><span>Flutter's build modes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/hot-reload"><div><span>Hot reload</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-31" role="button" aria-expanded="false" aria-controls="sidenav-31"><span>Resources</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31"><li class="nav-item"><a class="nav-link" href="/resources/faq"><div><span>FAQ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/books"><div><span>Books</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/videos"><div><span>Videos</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/courses"><div><span>Courses</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/bootstrap-into-dart"><div><span>Learn Dart</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/support"><div><span>Get support</span></div></a></li><div class="sidenav-divider"></div><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-31-8" role="button" aria-expanded="false" aria-controls="sidenav-31-8"><span>Contribute</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31-8"><li class="nav-item"><a class="nav-link" href="/resources/bug-reports"><div><span>Create useful bug reports</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://github.com/flutter/flutter/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"><div><span>Contribute to Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/design-docs"><div><span>Discover proposed features</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-31-9" role="button" aria-expanded="false" aria-controls="sidenav-31-9"><span>Reference</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31-9"><li class="nav-item"><a class="nav-link" href="/dash"><div><span>Who is Dash?</span></div></a></li><li class="nav-item"><a class="nav-link" href="/reference/widgets"><div><span>Widget index</span></div></a></li><li class="nav-item"><a class="nav-link" href="/reference/flutter-cli"><div><span>flutter CLI</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://api.flutter.dev" target="_blank" rel="noopener"><div><span>API docs</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li></ul></li></ul></nav></div><main class="site-content"><div id="site-toc--side" class="site-toc"><header class="site-toc__title">Contents</header><ul class="section-nav"><li class="toc-entry nav-item"><a class="nav-link" href="#1-prepare-your-game-for-multiplayer">1. Prepare your game for multiplayer</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#2-install-firestore">2. Install Firestore</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#3-initialize-firestore">3. Initialize Firestore</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#4-create-a-firestore-controller-class">4. Create a Firestore controller class</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#5-use-the-firestore-controller">5. Use the Firestore controller</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#6-test-the-game">6. Test the game</a><ul class="nav"><li class="toc-entry nav-item"><a class="nav-link" href="#troubleshooting">Troubleshooting</a></li></ul></li><li class="toc-entry nav-item"><a class="nav-link" href="#7-next-steps">7. Next steps</a></li></ul></div><article><header class="site-content__title"><h1 id="document-title">Add multiplayer support using Firestore</h1><nav class="breadcrumbs" aria-label="breadcrumb"><ol vocab="https://schema.org/" typeof="BreadcrumbList"><li class="breadcrumb-item" property="itemListElement" typeof="ListItem"><a href="/cookbook" property="item" typeof="WebPage"><span property="name">Cookbook</span></a><meta property="position" content="0"><span class="material-symbols child-icon" aria-hidden="true">chevron_right</span></li><li class="breadcrumb-item" property="itemListElement" typeof="ListItem"><a href="/cookbook/games" property="item" typeof="WebPage"><span property="name">Games</span></a><meta property="position" content="1"><span class="material-symbols child-icon" aria-hidden="true">chevron_right</span></li><li class="breadcrumb-item active" property="itemListElement" typeof="ListItem" aria-current="page"><a href="/cookbook/games/firestore-multiplayer" property="item" typeof="WebPage"><span property="name">Add multiplayer support using Firestore</span></a><meta property="position" content="2"></li></ol></nav></header><div id="site-toc--inline" class="site-toc"><header class="site-toc__title">Contents</header><ul class="section-nav"><li class="toc-entry"><a href="#1-prepare-your-game-for-multiplayer">1. Prepare your game for multiplayer</a></li><li class="toc-entry"><a href="#2-install-firestore">2. Install Firestore</a></li><li class="toc-entry"><a href="#3-initialize-firestore">3. Initialize Firestore</a></li><li class="toc-entry"><a href="#4-create-a-firestore-controller-class">4. Create a Firestore controller class</a></li><li class="toc-entry"><a href="#5-use-the-firestore-controller">5. Use the Firestore controller</a></li><li class="toc-entry"><a href="#6-test-the-game">6. Test the game</a><ul><li class="toc-entry"><a href="#troubleshooting">Troubleshooting</a></li></ul></li><li class="toc-entry"><a href="#7-next-steps">7. Next steps</a></li></ul></div> <?code-excerpt path-base="cookbook/games/firestore_multiplayer"?> <p>Multiplayer games need a way to synchronize game states between players. Broadly speaking, two types of multiplayer games exist:</p><ol><li><p><strong>High tick rate</strong>. These games need to synchronize game states many times per second with low latency. These would include action games, sports games, fighting games.</p></li><li><p><strong>Low tick rate</strong>. These games only need to synchronize game states occasionally with latency having less impact. These would include card games, strategy games, puzzle games.</p></li></ol><p>This resembles the differentiation between real-time versus turn-based games, though the analogy falls short. For example, real-time strategy games run鈥攁s the name suggests鈥攊n 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.</p><p><img src="/assets/images/docs/cookbook/multiplayer-two-mobiles.jpg" alt="An illustration of two mobile phones and a two-way arrow between them" class="site-illustration"></p><p>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 <em>don't make a good fit</em>. Pick a dedicated multiplayer server solution such as <a href="https://heroiclabs.com/nakama/">Nakama</a>. Nakama has a <a href="https://pub.dev/packages/nakama">Dart package</a>.</p><p>If you expect that your game requires a low tick rate of synchronization, continue reading.</p><p>This recipe demonstrates how to use the <a href="https://pub.dev/packages/cloud_firestore"><code>cloud_firestore</code> package</a> 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.</p><div class="header-wrapper"><h2 id="1-prepare-your-game-for-multiplayer">1. Prepare your game for multiplayer</h2><a class="heading-link" href="#1-prepare-your-game-for-multiplayer" aria-label="Link to '1. Prepare your game for multiplayer' section">#</a></div><p>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.</p><p><img src="/assets/images/docs/cookbook/multiplayer-card-game.jpg" alt="Screenshot of the card game" class="site-mobile-screenshot site-illustration"></p><p>To simplify this cookbook recipe, start with the <a href="https://github.com/flutter/games/tree/main/templates/card#readme"><code>card</code></a> template that you'll find in the <a href="https://github.com/flutter/games"><code>flutter/games</code> repository</a>. Run the following command to clone that repository:</p><div class="code-block-wrapper language-console"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#222222">git clone https://github.com/flutter/games.git</span></span></code></pre></div></div><p>Open the project in <code>templates/card</code>.</p><aside class="alert alert-info"><div class="alert-header"><i class="material-symbols" aria-hidden="true">info</i> <span>Note</span></div><div class="alert-content"><p>You can ignore this step and follow the recipe with your own game project. Adapt the code at appropriate places.</p></div></aside><div class="header-wrapper"><h2 id="2-install-firestore">2. Install Firestore</h2><a class="heading-link" href="#2-install-firestore" aria-label="Link to '2. Install Firestore' section">#</a></div><p><a href="https://cloud.google.com/firestore/">Cloud Firestore</a> 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.</p><p>If you want a quick, 15-minute primer on Cloud Firestore, check out the following video:</p><iframe width="560" height="315" src="https://www.youtube.com/embed/v_hR4K4auoQ" title="What is a NoSQL Database? Learn about Cloud Firestore" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen loading="lazy"></iframe><br><p><a href="https://www.youtube.com/watch/v_hR4K4auoQ" target="_blank" rel="noopener" title="Open 'What is a NoSQL Database? Learn about Cloud Firestore' video in new tab">What is a NoSQL Database? Learn about Cloud Firestore</a></p><p>To add Firestore to your Flutter project, follow the first two steps of the <a href="https://firebase.google.com/docs/firestore/quickstart">Get started with Cloud Firestore</a> guide:</p><ul><li><a href="https://firebase.google.com/docs/firestore/quickstart#create">Create a Cloud Firestore database</a></li><li><a href="https://firebase.google.com/docs/firestore/quickstart#set_up_your_development_environment">Set up your development environment</a></li></ul><p>The desired outcomes include:</p><ul><li>A Firestore database ready in the cloud, in <strong>Test mode</strong></li><li>A generated <code>firebase_options.dart</code> file</li><li>The appropriate plugins added to your <code>pubspec.yaml</code></li></ul><p>You <em>don't</em> 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.</p><div class="header-wrapper"><h2 id="3-initialize-firestore">3. Initialize Firestore</h2><a class="heading-link" href="#3-initialize-firestore" aria-label="Link to '3. Initialize Firestore' section">#</a></div><ol><li><p>Open <code>lib/main.dart</code> and import the plugins, as well as the <code>firebase_options.dart</code> file that was generated by <code>flutterfire configure</code> in the previous step.</p> <?code-excerpt "lib/main.dart (imports)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:cloud_firestore/cloud_firestore.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:firebase_core/firebase_core.dart'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'firebase_options.dart'</span><span style="color:#222222">;</span></span></code></pre></div></div></li><li><p>Add the following code just above the call to <code>runApp()</code> in <code>lib/main.dart</code>:</p> <?code-excerpt "lib/main.dart (initializeApp)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">WidgetsFlutterBinding</span><span style="color:#222222">.</span><span style="color:#6200EE">ensureInitialized</span><span style="color:#222222">();</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">await</span><span style="color:#0468D7"> Firebase</span><span style="color:#222222">.</span><span style="color:#6200EE">initializeApp</span><span style="color:#222222">(options: </span><span style="color:#0468D7">DefaultFirebaseOptions</span><span style="color:#222222">.currentPlatform);</span></span></code></pre></div></div><p>This ensures that Firebase is initialized on game startup.</p></li><li><p>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.</p><p>To do this with the <code>card</code> template, you can use the <code>provider</code> package (which is already installed as a dependency).</p><p>Replace the boilerplate <code>runApp(MyApp())</code> with the following:</p> <?code-excerpt "lib/main.dart (runApp)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#6200EE">runApp</span><span style="color:#222222">(</span><span style="color:#0468D7">Provider</span><span style="color:#222222">.</span><span style="color:#6200EE">value</span><span style="color:#222222">(value: </span><span style="color:#0468D7">FirebaseFirestore</span><span style="color:#222222">.instance, child: </span><span style="color:#0468D7">MyApp</span><span style="color:#222222">()));</span></span></code></pre></div></div><p>Put the provider above <code>MyApp</code>, not inside it. This enables you to test the app without Firebase.</p><aside class="alert alert-info"><div class="alert-header"><i class="material-symbols" aria-hidden="true">info</i> <span>Note</span></div><div class="alert-content"><p>In case you are <em>not</em> working with the <code>card</code> template, you must either <a href="https://pub.dev/packages/provider/install">install the <code>provider</code> package</a> or use your own method of accessing the <code>FirebaseFirestore</code> instance from various parts of your codebase.</p></div></aside></li></ol><div class="header-wrapper"><h2 id="4-create-a-firestore-controller-class">4. Create a Firestore controller class</h2><a class="heading-link" href="#4-create-a-firestore-controller-class" aria-label="Link to '4. Create a Firestore controller class' section">#</a></div><p>Though you can talk to Firestore directly, you should write a dedicated controller class to make the code more readable and maintainable.</p><p>How you implement the controller depends on your game and on the exact design of your multiplayer experience. For the case of the <code>card</code> 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.</p><p><img src="/assets/images/docs/cookbook/multiplayer-areas.jpg" alt="Screenshot of the card game, with arrows pointing to playing areas" class="site-mobile-screenshot site-illustration"></p><p>To create a controller, copy, then paste the following code into a new file called <code>lib/multiplayer/firestore_controller.dart</code>.</p> <?code-excerpt "lib/multiplayer/firestore_controller.dart"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'dart:async'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:cloud_firestore/cloud_firestore.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:flutter/foundation.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:logging/logging.dart'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> '../game_internals/board_state.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> '../game_internals/playing_area.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> '../game_internals/playing_card.dart'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> FirestoreController</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> static</span><span style="color:#BD2314"> final</span><span style="color:#222222"> _log = </span><span style="color:#0468D7">Logger</span><span style="color:#222222">(</span><span style="color:#0C7064">'FirestoreController'</span><span style="color:#222222">);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#0468D7"> FirebaseFirestore</span><span style="color:#222222"> instance;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#0468D7"> BoardState</span><span style="color:#222222"> boardState;</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// For now, there is only one match. But in order to be ready</span></span> <span class="line"><span style="color:#6E6E70"> /// for match-making, put it in a Firestore collection called matches.</span></span> <span class="line"><span style="color:#BD2314"> late</span><span style="color:#BD2314"> final</span><span style="color:#222222"> _matchRef = instance.</span><span style="color:#6200EE">collection</span><span style="color:#222222">(</span><span style="color:#0C7064">'matches'</span><span style="color:#222222">).</span><span style="color:#6200EE">doc</span><span style="color:#222222">(</span><span style="color:#0C7064">'match_1'</span><span style="color:#222222">);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> late</span><span style="color:#BD2314"> final</span><span style="color:#222222"> _areaOneRef = _matchRef</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">collection</span><span style="color:#222222">(</span><span style="color:#0C7064">'areas'</span><span style="color:#222222">)</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">doc</span><span style="color:#222222">(</span><span style="color:#0C7064">'area_one'</span><span style="color:#222222">)</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">withConverter</span><span style="color:#222222"><</span><span style="color:#0468D7">List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">>>(</span></span> <span class="line"><span style="color:#222222"> fromFirestore: _cardsFromFirestore,</span></span> <span class="line"><span style="color:#222222"> toFirestore: _cardsToFirestore,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> late</span><span style="color:#BD2314"> final</span><span style="color:#222222"> _areaTwoRef = _matchRef</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">collection</span><span style="color:#222222">(</span><span style="color:#0C7064">'areas'</span><span style="color:#222222">)</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">doc</span><span style="color:#222222">(</span><span style="color:#0C7064">'area_two'</span><span style="color:#222222">)</span></span> <span class="line"><span style="color:#222222"> .</span><span style="color:#6200EE">withConverter</span><span style="color:#222222"><</span><span style="color:#0468D7">List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">>>(</span></span> <span class="line"><span style="color:#222222"> fromFirestore: _cardsFromFirestore,</span></span> <span class="line"><span style="color:#222222"> toFirestore: _cardsToFirestore,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> StreamSubscription</span><span style="color:#222222">? _areaOneFirestoreSubscription;</span></span> <span class="line"><span style="color:#0468D7"> StreamSubscription</span><span style="color:#222222">? _areaTwoFirestoreSubscription;</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> StreamSubscription</span><span style="color:#222222">? _areaOneLocalSubscription;</span></span> <span class="line"><span style="color:#0468D7"> StreamSubscription</span><span style="color:#222222">? _areaTwoLocalSubscription;</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> FirestoreController</span><span style="color:#222222">({</span><span style="color:#BD2314">required</span><span style="color:#BD2314"> this</span><span style="color:#222222">.instance, </span><span style="color:#BD2314">required</span><span style="color:#BD2314"> this</span><span style="color:#222222">.boardState}) {</span></span> <span class="line"><span style="color:#6E6E70"> // Subscribe to the remote changes (from Firestore).</span></span> <span class="line"><span style="color:#222222"> _areaOneFirestoreSubscription = _areaOneRef.</span><span style="color:#6200EE">snapshots</span><span style="color:#222222">().</span><span style="color:#6200EE">listen</span><span style="color:#222222">((snapshot) {</span></span> <span class="line"><span style="color:#6200EE"> _updateLocalFromFirestore</span><span style="color:#222222">(boardState.areaOne, snapshot);</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"><span style="color:#222222"> _areaTwoFirestoreSubscription = _areaTwoRef.</span><span style="color:#6200EE">snapshots</span><span style="color:#222222">().</span><span style="color:#6200EE">listen</span><span style="color:#222222">((snapshot) {</span></span> <span class="line"><span style="color:#6200EE"> _updateLocalFromFirestore</span><span style="color:#222222">(boardState.areaTwo, snapshot);</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // Subscribe to the local changes in game state.</span></span> <span class="line"><span style="color:#222222"> _areaOneLocalSubscription = boardState.areaOne.playerChanges.</span><span style="color:#6200EE">listen</span><span style="color:#222222">((_) {</span></span> <span class="line"><span style="color:#6200EE"> _updateFirestoreFromLocalAreaOne</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"><span style="color:#222222"> _areaTwoLocalSubscription = boardState.areaTwo.playerChanges.</span><span style="color:#6200EE">listen</span><span style="color:#222222">((_) {</span></span> <span class="line"><span style="color:#6200EE"> _updateFirestoreFromLocalAreaTwo</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'Initialized'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> dispose</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#222222"> _areaOneFirestoreSubscription?.</span><span style="color:#6200EE">cancel</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> _areaTwoFirestoreSubscription?.</span><span style="color:#6200EE">cancel</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> _areaOneLocalSubscription?.</span><span style="color:#6200EE">cancel</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> _areaTwoLocalSubscription?.</span><span style="color:#6200EE">cancel</span><span style="color:#222222">();</span></span> <span class="line"></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'Disposed'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Takes the raw JSON snapshot coming from Firestore and attempts to</span></span> <span class="line"><span style="color:#6E6E70"> /// convert it into a list of </span><span style="color:#222222">[PlayingCard]</span><span style="color:#6E6E70">s.</span></span> <span class="line"><span style="color:#0468D7"> List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">> </span><span style="color:#6200EE">_cardsFromFirestore</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> DocumentSnapshot</span><span style="color:#222222"><</span><span style="color:#0468D7">Map</span><span style="color:#222222"><</span><span style="color:#0468D7">String</span><span style="color:#222222">, </span><span style="color:#0468D7">dynamic</span><span style="color:#222222">>> snapshot,</span></span> <span class="line"><span style="color:#0468D7"> SnapshotOptions</span><span style="color:#222222">? options,</span></span> <span class="line"><span style="color:#222222"> ) {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> data = snapshot.</span><span style="color:#6200EE">data</span><span style="color:#222222">()?[</span><span style="color:#0C7064">'cards'</span><span style="color:#222222">] </span><span style="color:#BD2314">as</span><span style="color:#0468D7"> List</span><span style="color:#222222">?;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (data == </span><span style="color:#0C7064">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">info</span><span style="color:#222222">(</span><span style="color:#0C7064">'No data found on Firestore, returning empty list'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#BD2314"> return</span><span style="color:#222222"> [];</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> list = </span><span style="color:#0468D7">List</span><span style="color:#222222">.</span><span style="color:#6200EE">castFrom</span><span style="color:#222222"><</span><span style="color:#0468D7">Object</span><span style="color:#222222">?, </span><span style="color:#0468D7">Map</span><span style="color:#222222"><</span><span style="color:#0468D7">String</span><span style="color:#222222">, </span><span style="color:#0468D7">Object</span><span style="color:#222222">?>>(data);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> return</span><span style="color:#222222"> list.</span><span style="color:#6200EE">map</span><span style="color:#222222">((raw) => </span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">.</span><span style="color:#6200EE">fromJson</span><span style="color:#222222">(raw)).</span><span style="color:#6200EE">toList</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#BD2314">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#BD2314"> throw</span><span style="color:#0468D7"> FirebaseControllerException</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0C7064"> 'Failed to parse data from Firestore: </span><span style="color:#0C7064">$</span><span style="color:#222222">e</span><span style="color:#0C7064">'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Takes a list of </span><span style="color:#222222">[PlayingCard]</span><span style="color:#6E6E70">s and converts it into a JSON object</span></span> <span class="line"><span style="color:#6E6E70"> /// that can be saved into Firestore.</span></span> <span class="line"><span style="color:#0468D7"> Map</span><span style="color:#222222"><</span><span style="color:#0468D7">String</span><span style="color:#222222">, </span><span style="color:#0468D7">Object</span><span style="color:#222222">?> </span><span style="color:#6200EE">_cardsToFirestore</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">> cards,</span></span> <span class="line"><span style="color:#0468D7"> SetOptions</span><span style="color:#222222">? options,</span></span> <span class="line"><span style="color:#222222"> ) {</span></span> <span class="line"><span style="color:#BD2314"> return</span><span style="color:#222222"> {</span><span style="color:#0C7064">'cards'</span><span style="color:#222222">: cards.</span><span style="color:#6200EE">map</span><span style="color:#222222">((c) => c.</span><span style="color:#6200EE">toJson</span><span style="color:#222222">()).</span><span style="color:#6200EE">toList</span><span style="color:#222222">()};</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Updates Firestore with the local state of </span><span style="color:#222222">[area]</span><span style="color:#6E6E70">.</span></span> <span class="line"><span style="color:#0468D7"> Future</span><span style="color:#222222"><</span><span style="color:#BD2314">void</span><span style="color:#222222">> </span><span style="color:#6200EE">_updateFirestoreFromLocal</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> PlayingArea</span><span style="color:#222222"> area,</span></span> <span class="line"><span style="color:#0468D7"> DocumentReference</span><span style="color:#222222"><</span><span style="color:#0468D7">List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">>> ref,</span></span> <span class="line"><span style="color:#222222"> ) </span><span style="color:#BD2314">async</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'Updating Firestore with local data (</span><span style="color:#0C7064">${</span><span style="color:#222222">area</span><span style="color:#0C7064">.</span><span style="color:#222222">cards</span><span style="color:#0C7064">}</span><span style="color:#0C7064">) ...'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#BD2314"> await</span><span style="color:#222222"> ref.</span><span style="color:#6200EE">set</span><span style="color:#222222">(area.cards);</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'... done updating.'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#BD2314">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#BD2314"> throw</span><span style="color:#0468D7"> FirebaseControllerException</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0C7064"> 'Failed to update Firestore with local data (</span><span style="color:#0C7064">${</span><span style="color:#222222">area</span><span style="color:#0C7064">.</span><span style="color:#222222">cards</span><span style="color:#0C7064">}</span><span style="color:#0C7064">): </span><span style="color:#0C7064">$</span><span style="color:#222222">e</span><span style="color:#0C7064">'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Sends the local state of </span><span style="color:#222222">`boardState.areaOne`</span><span style="color:#6E6E70"> to Firestore.</span></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> _updateFirestoreFromLocalAreaOne</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#6200EE"> _updateFirestoreFromLocal</span><span style="color:#222222">(boardState.areaOne, _areaOneRef);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Sends the local state of </span><span style="color:#222222">`boardState.areaTwo`</span><span style="color:#6E6E70"> to Firestore.</span></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> _updateFirestoreFromLocalAreaTwo</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#6200EE"> _updateFirestoreFromLocal</span><span style="color:#222222">(boardState.areaTwo, _areaTwoRef);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> /// Updates the local state of </span><span style="color:#222222">[area]</span><span style="color:#6E6E70"> with the data from Firestore.</span></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> _updateLocalFromFirestore</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> PlayingArea</span><span style="color:#222222"> area,</span></span> <span class="line"><span style="color:#0468D7"> DocumentSnapshot</span><span style="color:#222222"><</span><span style="color:#0468D7">List</span><span style="color:#222222"><</span><span style="color:#0468D7">PlayingCard</span><span style="color:#222222">>> snapshot,</span></span> <span class="line"><span style="color:#222222"> ) {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'Received new data from Firestore (</span><span style="color:#0C7064">${</span><span style="color:#222222">snapshot</span><span style="color:#0C7064">.</span><span style="color:#6200EE">data</span><span style="color:#0C7064">()}</span><span style="color:#0C7064">)'</span><span style="color:#222222">);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> cards = snapshot.</span><span style="color:#6200EE">data</span><span style="color:#222222">() ?? [];</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (</span><span style="color:#6200EE">listEquals</span><span style="color:#222222">(cards, area.cards)) {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'No change'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#BD2314">else</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">fine</span><span style="color:#222222">(</span><span style="color:#0C7064">'Updating local data with Firestore data (</span><span style="color:#0C7064">$</span><span style="color:#222222">cards</span><span style="color:#0C7064">)'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> area.</span><span style="color:#6200EE">replaceWith</span><span style="color:#222222">(cards);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> FirebaseControllerException</span><span style="color:#BD2314"> implements</span><span style="color:#0468D7"> Exception</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#0468D7"> String</span><span style="color:#222222"> message;</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> FirebaseControllerException</span><span style="color:#222222">(</span><span style="color:#BD2314">this</span><span style="color:#222222">.message);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> @override</span></span> <span class="line"><span style="color:#0468D7"> String</span><span style="color:#6200EE"> toString</span><span style="color:#222222">() => </span><span style="color:#0C7064">'FirebaseControllerException: </span><span style="color:#0C7064">$</span><span style="color:#222222">message</span><span style="color:#0C7064">'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Notice the following features of this code:</p><ul><li><p>The controller's constructor takes a <code>BoardState</code>. This enables the controller to manipulate the local state of the game.</p></li><li><p>The controller subscribes to both local changes to update Firestore and to remote changes to update the local state and UI.</p></li><li><p>The fields <code>_areaOneRef</code> and <code>_areaTwoRef</code> are Firebase document references. They describe where the data for each area resides, and how to convert between the local Dart objects (<code>List<PlayingCard></code>) and remote JSON objects (<code>Map<String, dynamic></code>). The Firestore API lets us subscribe to these references with <code>.snapshots()</code>, and write to them with <code>.set()</code>.</p></li></ul><div class="header-wrapper"><h2 id="5-use-the-firestore-controller">5. Use the Firestore controller</h2><a class="heading-link" href="#5-use-the-firestore-controller" aria-label="Link to '5. Use the Firestore controller' section">#</a></div><ol><li><p>Open the file responsible for starting the play session: <code>lib/play_session/play_session_screen.dart</code> in the case of the <code>card</code> template. You instantiate the Firestore controller from this file.</p></li><li><p>Import Firebase and the controller:</p> <?code-excerpt "lib/play_session/play_session_screen.dart (imports)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> 'package:cloud_firestore/cloud_firestore.dart'</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314">import</span><span style="color:#0C7064"> '../multiplayer/firestore_controller.dart'</span><span style="color:#222222">;</span></span></code></pre></div></div></li><li><p>Add a nullable field to the <code>_PlaySessionScreenState</code> class to contain a controller instance:</p> <?code-excerpt "lib/play_session/play_session_screen.dart (controller)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">FirestoreController</span><span style="color:#222222">? _firestoreController;</span></span></code></pre></div></div></li><li><p>In the <code>initState()</code> method of the same class, add code that tries to read the FirebaseFirestore instance and, if successful, constructs the controller. You added the <code>FirebaseFirestore</code> instance to <code>main.dart</code> in the <em>Initialize Firestore</em> step.</p> <?code-excerpt "lib/play_session/play_session_screen.dart (init-state)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">final</span><span style="color:#222222"> firestore = context.</span><span style="color:#6200EE">read</span><span style="color:#222222"><</span><span style="color:#0468D7">FirebaseFirestore</span><span style="color:#222222">?>();</span></span> <span class="line"><span style="color:#BD2314">if</span><span style="color:#222222"> (firestore == </span><span style="color:#0C7064">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#222222"> _log.</span><span style="color:#6200EE">warning</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0C7064"> "Firestore instance wasn't provided. "</span></span> <span class="line"><span style="color:#0C7064"> 'Running without _firestoreController.'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222">} </span><span style="color:#BD2314">else</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#222222"> _firestoreController = </span><span style="color:#0468D7">FirestoreController</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> instance: firestore,</span></span> <span class="line"><span style="color:#222222"> boardState: _boardState,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div></li><li><p>Dispose of the controller using the <code>dispose()</code> method of the same class.</p> <?code-excerpt "lib/play_session/play_session_screen.dart (dispose)"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#222222">_firestoreController?.</span><span style="color:#6200EE">dispose</span><span style="color:#222222">();</span></span></code></pre></div></div></li></ol><div class="header-wrapper"><h2 id="6-test-the-game">6. Test the game</h2><a class="heading-link" href="#6-test-the-game" aria-label="Link to '6. Test the game' section">#</a></div><ol><li><p>Run the game on two separate devices or in 2 different windows on the same device.</p></li><li><p>Watch how adding a card to an area on one device makes it appear on the other one.</p></li><li><p>Open the <a href="https://console.firebase.google.com/">Firebase web console</a> and navigate to your project's Firestore Database.</p></li><li><p>Watch how it updates the data in real time. You can even edit the data in the console and see all running clients update.</p><p><img src="/assets/images/docs/cookbook/multiplayer-firebase-data.png" alt="Screenshot of the Firebase Firestore data view"></p></li></ol><div class="header-wrapper"><h3 id="troubleshooting">Troubleshooting</h3><a class="heading-link" href="#troubleshooting" aria-label="Link to 'Troubleshooting' section">#</a></div><p>The most common issues you might encounter when testing Firebase integration include the following:</p><ul><li><p><strong>The game crashes when trying to reach Firebase.</strong></p><ul><li>Firebase integration hasn't been properly set up. Revisit <em>Step 2</em> and make sure to run <code>flutterfire configure</code> as part of that step.</li></ul></li><li><p><strong>The game doesn't communicate with Firebase on macOS.</strong></p><ul><li>By default, macOS apps don't have internet access. Enable <a href="/data-and-backend/networking#macos">internet entitlement</a> first.</li></ul></li></ul><div class="header-wrapper"><h2 id="7-next-steps">7. Next steps</h2><a class="heading-link" href="#7-next-steps" aria-label="Link to '7. Next steps' section">#</a></div><p>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.</p><p><img src="/assets/images/docs/cookbook/multiplayer-two-mobiles.jpg" alt="An illustration of two mobile phones and a two-way arrow between them" class="site-illustration"></p><p>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 <code>_matchRef</code>, too, like who the players are and whose turn it is. If you're unsure where to start, follow <a href="https://codelabs.developers.google.com/?product=flutter&text=firestore">a Firestore codelab or two</a> to familiarize yourself with the API.</p><p>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 <a href="https://firebase.google.com/docs/auth/flutter/start">built-in way to authenticate users</a> and the Firestore database structure can handle multiple matches. Instead of a single <code>match_1</code>, you can populate the matches collection with as many records as needed.</p><p><img src="/assets/images/docs/cookbook/multiplayer-firebase-match.png" alt="Screenshot of the Firebase Firestore data view with additional matches"></p><p>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.</p><p id="page-github-links"><span>Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2025-02-12.</span> <a href="https://github.com/flutter/website/tree/main/src/content/cookbook/games/firestore-multiplayer.md" target="_blank" rel="noopener">View source</a> <span>or </span><a href="https://github.com/flutter/website/issues/new?template=1_page_issue.yml&&page-url=https://docs.flutter.dev/cookbook/games/firestore-multiplayer/&page-source=https://github.com/flutter/website/tree/main/src/content/cookbook/games/firestore-multiplayer.md" title="Report an issue with this page" target="_blank" rel="noopener">report an issue</a>.</p></article></main></div><footer id="site-footer"><div class="footer-section footer-main"><a class="brand" href="https://flutter.dev"><img src="/assets/images/branding/flutter/logo+text/horizontal/white.svg" alt="Flutter logo" width="164"></a><div class="footer-social-links"><a class="icon-button" href="https://medium.com/flutter" target="_blank" rel="noopener" title="Flutter's Medium blog"><svg><use href="/assets/images/social/medium.svg#medium"></use></svg> </a><a class="icon-button" href="https://youtube.com/@flutterdev" target="_blank" rel="noopener" title="Flutter's YouTube"><svg><use href="/assets/images/social/youtube.svg#youtube"></use></svg> </a><a class="icon-button" href="https://github.com/flutter" target="_blank" rel="noopener" title="Flutter's GitHub"><svg><use href="/assets/images/social/github.svg#github"></use></svg> </a><a class="icon-button" href="https://bsky.app/profile/flutter.dev" target="_blank" rel="noopener" title="Flutter's Bluesky"><svg><use href="/assets/images/social/bluesky.svg#bluesky"></use></svg> </a><a class="icon-button" href="https://twitter.com/FlutterDev" target="_blank" rel="noopener" title="Flutter's X (Twitter)"><svg><use href="/assets/images/social/x.svg#x"></use></svg></a></div></div><div class="footer-section footer-tray"><div class="footer-licenses">Except as otherwise noted, this site is licensed under a <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>, and code samples are licensed under the <a href="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</a>.</div><div class="footer-utility-links"><ul><li><a href="/tos" title="Terms of use">Terms</a></li><li><a href="/brand" title="Brand usage guidelines">Brand</a></li><li><a href="https://policies.google.com/privacy" target="_blank" rel="noopener" title="Privacy policy">Privacy</a></li><li><a href="/security" title="Security philosophy and practices">Security</a></li></ul></div></div></footer></div><script src="/assets/js/tabs.js?v=4"></script><script src="/assets/js/archive.js?v=4"></script><script src="/assets/js/main.js?v=4"></script></body></html>