Skip to main content

Firebase

Three 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.

The last two 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.

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. The warning names these checks, 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.

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

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, SMF points the phase at build/ios/SourcePackages. It changes that path in ios/Runner.xcodeproj/project.pbxproj and nothing else, and an app without the phase 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 README of the app has the command.

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, run the fix of the phase afterwards, as above.

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. 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.