Skip to main content

Firebase

Four modules bring Firebase into an app:

ModuleWhat it adds
firebase_coreFirebase itself: firebase_core, the options of the Firebase app, and its initialization at start-up.
firebase_crashlyticsCrash reporting with Firebase Crashlytics.
firebase_analyticsAnalytics with Firebase Analytics, including screen views.
firebase_authSign-in with Firebase Authentication: an email address and a password, or an anonymous user. The feature sign_in, the screens of sign-in, brings it.

The last three depend on firebase_core, which smf create adds with them.

What SMF generates​

firebase_core adds lib/firebase_options.dart with DefaultFirebaseOptions, the options of the Firebase app of each platform, and makes bootstrap() initialize Firebase with them before the other services:

await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

The options come from your Firebase project, where flutterfire configure of the FlutterFire CLI registers the app. Until that command runs, lib/firebase_options.dart is a placeholder in the form that flutterfire writes, so the app compiles but stops at start-up with an UnsupportedError. The FlutterFire CLI later fills the placeholder in place.

The module also asks for iOS 15.0 or newer, which the Firebase SDKs need and the app has already. It adds a Firebase section to the README of the app with the commands below.

Before generation: the machine​

flutterfire configure needs a few tools, so SMF checks the machine before it generates an app with Firebase:

CheckWhat it looks for
Firebase CLIThe firebase command, which the FlutterFire CLI runs to reach your Firebase projects. SMF runs firebase --version to see that it works.
Firebase loginAn account logged in to the Firebase CLI, as firebase login:list --json reports it. SMF then lists your Firebase projects with firebase projects:list --debug to see that Google still accepts the login.
FlutterFire CLIflutterfire_cli 1.4.1 or a later 1.x, activated globally, as dart pub global activate flutterfire_cli 1.4.1 does.
Xcode project toolsOn macOS, Ruby with the gem xcodeproj 1.23.0 or newer, with which the FlutterFire CLI sets up the iOS app in its Xcode project.
Setup of the Xcode project on a MacOn other systems, a warning: the FlutterFire CLI changes the Xcode project only on macOS.
Firebase CLI 15.6.0 or laterFor an app with firebase_auth: the first Firebase CLI that can enable sign-in methods, which SMF reads from firebase --version. SMF does not update the Firebase CLI. For an older one, it tells which command it found, in which version, and how to update it.

The app compiles without any of them, so none of them stops the run. A missing one is a warning with instructions, and smf create --explain shows the state of each.

In a terminal, unless the run has --skip-external-setup, SMF offers to fix what it can, and asks before each step. Each question says what the check found and how to fix it by hand, then asks whether to set it up now.

It can install the Firebase CLI with npm, also when a firebase command is on the PATH but does not run, such as one that needs another Node.js. The question then says how firebase --version ended. When Node.js is missing or older than 20, it installs Node.js first: on macOS with nvm when the Node.js on the PATH is one of nvm, or else with Homebrew or nvm, with nvm on Linux, and with winget, Chocolatey, Scoop or the portable ZIP of Node.js on Windows. The installation can take minutes, and its progress shows what it is doing. When the directory of the Firebase CLI is not on the PATH of new terminals, the installation adds it to the profile of the shell or, on Windows, to the PATH of the user. On Linux it also moves the global directory of npm to ~/.npm-global, so that it needs no sudo, and adds a firebase command to ~/.local/bin that runs the Firebase CLI with the Node.js that installed it, whatever Node.js a terminal has, such as another default version of nvm. On macOS, it adds that command, and ~/.local/bin to the PATH of new terminals, when it installs the Firebase CLI with a Node.js of nvm. SMF lists these changes afterwards. When npm fails on macOS or Linux, SMF offers the standalone binary of the Firebase CLI instead, curl -sL https://firebase.tools | bash, which may ask for your password to write to /usr/local/bin.

It can log you in with firebase login, which asks its own questions in the terminal. firebase login waits for the browser to come back to a server on the machine, which a browser on another machine cannot reach. So when SSH_CONNECTION, SSH_CLIENT or SSH_TTY has a value, as in a session over SSH, SMF runs firebase login --no-localhost instead. That command gives a link to open on any device and asks for the authorization code shown there. On a remote machine where none of them has a value, log in yourself with firebase login --no-localhost.

A login of the Firebase CLI can expire or be revoked. firebase login:list still lists such an account, but the Firebase CLI can no longer reach your projects, so flutterfire configure finds no project and offers to create one. That is why SMF checks the login with firebase projects:list --debug, which only reads. The Firebase CLI reports the same error whether Google rejects the login or cannot be reached at all, and only its debug output tells the two apart. If Google rejects the login, SMF says that it has expired or is no longer valid and offers to log you in again with firebase login --reauth, or with firebase login --reauth --no-localhost over SSH. Plain firebase login would only say that you are logged in already. If the command cannot reach Google, as on a machine without a network, SMF says that it could not check the login and why, and leaves the configuration for later. The check needs the network and usually takes a few seconds; SMF shows the progress "Checking the machine" while the checks run. On a network that drops requests without answering, SMF gives up on the listing after 40 seconds and says that it could not check the login. Ctrl-C stops the check, along with the whole run.

It can activate flutterfire_cli 1.4.1 when no version is active or an older one is. The question names the version that is active and says that 1.4.1 takes its place:

FlutterFire CLI 1.4.1 or a later 1.x is needed by firebase_core, but flutterfire_cli 1.4.0 is active. Activate 1.4.1 in its place with "dart pub global activate flutterfire_cli 1.4.1". Set it up now?

SMF never replaces a newer major version, which other apps may need, and only tells you how to activate 1.4.1 in that case.

SMF cannot install the gem xcodeproj for you. Install it with gem install xcodeproj, with a Ruby version manager or, for the Ruby of macOS, with sudo.

note

On Windows, SMF installs with a PowerShell script. CI runs it on Windows with the Node.js of the machine, but the installation of Node.js by the script, with winget, Chocolatey, Scoop or the portable ZIP, has not run on Windows yet.

After generation: flutterfire configure​

Once the app has its packages, SMF shows the command below and asks whether to run it now. It runs the command in the app with the terminal attached, so the FlutterFire CLI can ask you for the Firebase project:

dart pub global run flutterfire_cli:flutterfire configure \
--platforms=android,ios \
--overwrite-firebase-options \
--ios-bundle-id=com.example.my-app \
--android-package-name=com.example.my_app

The FlutterFire CLI registers the Android and iOS apps in the project and writes lib/firebase_options.dart, android/app/google-services.json and firebase.json, and on macOS ios/Runner/GoogleService-Info.plist and the changes to the Xcode project. The command gives the FlutterFire CLI the ids of the apps that SMF generated. Without them, it reads the iOS bundle id from the Xcode project, but only when the id is not in quotes, and after the first configuration a bundle id with a hyphen, such as com.example.my-app, is in quotes, so configuring the app again would ask for it. SMF leaves out an id that the FlutterFire CLI would not take in an option, such as an Android id with an underscore in its first part, and the FlutterFire CLI reads that one from the app.

SMF prints the command for later instead of running it in these cases:

  • The run has no terminal or has --no-input, and the FlutterFire CLI needs you to choose a project.
  • The run has --skip-external-setup.
  • A check that the command needs did not pass: the Firebase CLI, the login, the FlutterFire CLI or, on macOS, the gem xcodeproj. The command would fail, so SMF does not ask about it. SMF names these checks with the command, also when the run has no terminal or skips external setup, so you know what to set up before you run the command.
  • You answer no, or the command fails.

A command that you or the options of the run left for later is listed after the line that the app is created. A command that failed, or that only a check kept from running, is a warning. The machine and external setup shows both.

The app is complete either way. Run the command in its directory when you are ready.

In an app with firebase_auth, a second question follows once flutterfire configure configured the app: whether to enable the sign-in methods in the project that you chose.

Crashlytics and flutter build ipa​

For an app with Crashlytics, the FlutterFire CLI adds a build phase to the Xcode project on macOS that uploads the debug symbols with the upload script of Crashlytics. Flutter keeps the Swift packages of the app, and the script with them, in build/ios/SourcePackages. The phase of flutterfire_cli 1.4.1 looks for the script in the build directory of Xcode, which flutter run and flutter build ios set to build/ios, but flutter build ipa does not, so the archive fails in that phase.

So right after flutterfire configure, on macOS only, firebase_crashlytics points the phase at build/ios/SourcePackages, with a step that continues the step of firebase_core. It changes that path in ios/Runner.xcodeproj/project.pbxproj and nothing else, and a project without the path, such as one that the fix changed already, stays as it is. The fix is part of the configuration: when flutterfire configure does not run, the fix waits with it, and SMF prints its command after that of flutterfire configure.

flutterfire configure writes the phase again each time it runs, so after you configure the app yourself on macOS, run the fix too. The command is in the Crashlytics section of the README of the app.

Sign-in methods of Firebase Authentication​

An app with firebase_auth needs one more thing than the configuration: the ways to sign in are enabled in the Firebase project, not in the code. That is Email/Password in every mode of the app, and Anonymous when the mode of the app is anonymous. Until they are enabled, signing in and signing up fail with the reason notConfigured, and the developerHint of the failure tells how to enable them. In the mode anonymous, the app also starts without a user.

The module generates a script of the app that enables them, tool/enable_firebase_sign_in.dart. Right after flutterfire configure configured the app, smf create asks whether to run it:

Enabling the sign-in methods of the app in its Firebase project (dart tool/enable_firebase_sign_in.dart), for firebase_auth. The Firebase CLI adds a web app named "Default Web App" to a project that has no web app, because it enables the methods through one (firebase/firebase-tools#11250). The page https://console.firebase.google.com/project/_/authentication/providers of the Firebase console enables them without it. Run it now?

SMF prints the command for later instead of asking in these cases:

  • flutterfire configure did not run, or failed, or you left it for later. The script waits with it.
  • The run has no terminal, has --no-input or has --skip-external-setup.
  • The Firebase CLI of the machine is missing or older than 15.6.0.

To enable the methods later, for another Firebase project, or after you changed the mode of the app to anonymous, run the script in the directory of the app:

dart tool/enable_firebase_sign_in.dart
What the script does
The projectIt reads the id of the project from firebase.json, where flutterfire configure wrote it, and says which project it enables the methods for. --project <id> names another. It stops when firebase.json has no project or several, unless you name one.
The modeIt reads the mode from authMode in lib/core/auth/app_session.dart, and enables Anonymous only in the mode anonymous. It stops when that file does not tell the mode.
The methodsIt enables them with firebase deploy --only auth of the Firebase CLI, which enables what its configuration names. It never disables a method.
The files of the appIt changes none. It runs the Firebase CLI in a temporary directory, with a configuration of its own there, and removes the directory. firebase.json of the app stays as it is, so a firebase deploy of the app does not touch the sign-in methods.
The accountIn that temporary directory, an account that firebase login:use chose for the directory of the app does not apply. --account <email> names the account.
Other argumentsThey go to the Firebase CLI as they are, such as --debug.
Ctrl+CThe script lets the Firebase CLI end, removes the temporary directory and exits with the code 130.

The Firebase CLI adds a web app named "Default Web App" to a project that has no web app, because it enables the methods through one (firebase/firebase-tools#11250). If you do not want that web app, enable the methods in the Firebase console instead: open Authentication > Sign-in method of the project that the app is configured for, and enable Email/Password, and Anonymous for the mode anonymous.

The ways to sign in are a setting of the project. A project that you configure the app for later needs them enabled too.

Firebase sends the message with which a user sets a new password from a template of the project, under Authentication > Templates. In an app with languages, the app asks Firebase for the message in the language that it is in.

Configure again, or on another machine​

To configure the app for another Firebase project, or to set it up on another machine, run in its directory the commands of the README of the app:

dart pub global activate flutterfire_cli 1.4.1
firebase login
flutterfire configure --platforms=android,ios --overwrite-firebase-options --ios-bundle-id=com.example.my-app --android-package-name=com.example.my_app

Skip the activation if dart pub global list shows flutterfire_cli 1.4.1 or a later 1.x already. If firebase login says that you are logged in already but flutterfire configure finds no Firebase project, your login has probably expired. Run firebase login --reauth. If you change the ids of the app, change them in the command too. With Crashlytics on macOS, then run the fix of the phase, which the Crashlytics section of the README gives. With firebase_auth, then run dart tool/enable_firebase_sign_in.dart for the new project.

The build phases that the FlutterFire CLI adds to the Xcode project, such as the upload of the debug symbols of Crashlytics, run flutterfire from ~/.pub-cache/bin. Every machine that builds such an app for iOS needs the FlutterFire CLI activated globally.

An app configured with flutterfire_cli 1.4.0​

The phase for Crashlytics that flutterfire_cli 1.4.0 adds does not find the upload script where Flutter puts the Swift packages, so flutter run and flutter build ios fail in that phase, even after you activate 1.4.1. Activate 1.4.1 and configure the app again on a Mac, with the ids of its apps as above, and the new phase replaces the old one. Then run the fix of the phase for flutter build ipa.

Linux and Windows​

On a system other than macOS, the FlutterFire CLI registers the iOS app and writes its options into lib/firebase_options.dart, but writes no GoogleService-Info.plist and leaves the Xcode project as it is, without the build phases it adds for some Firebase packages. Run flutterfire configure again on a Mac before building the iOS app there. SMF warns about this before it generates the app.

In CI​

A run without a terminal cannot choose a Firebase project, so in CI smf create generates the app with the placeholder options and prints the command for later, and after it the command that enables the sign-in methods of an app with firebase_auth. Such a run installs nothing either. Add --skip-external-setup anyway, as the command in scripts and CI does. It also holds back the steps of other modules that need an external service but ask nothing, which a run without a terminal would otherwise start.