简体   繁体   中英

How do I use FlutterFire with a emulator demo project?

I am using the firebase emulator with demo project , without any actual firebase project resource created. I am doing so with the following command...

firebase emulators:start --project demo-test --only firestore

This successfully starts a Firebase emulator instanceat localhost:8080.

Then I am connecting to the firebase in my flutter app as follows...

await Firebase.initializeApp();
FirebaseFirestore.instance.settings = const Settings(
  host: 'localhost:8080',
  sslEnabled: false,
  persistenceEnabled: false,
);

When initializeApp is run, I get the following error...

E/flutter ( 7724): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: [core/not-initialized] Firebase has not been correctly initialized.
E/flutter ( 7724):
E/flutter ( 7724): Usually this means you've attempted to use a Firebase service before calling `Firebase.initializeApp`.
E/flutter ( 7724):
E/flutter ( 7724): View the documentation for more information: https://firebase.flutter.dev/docs/overview#initialization
E/flutter ( 7724):
E/flutter ( 7724): #0      MethodChannelFirebase.initializeApp
package:firebase_core_platform_interface/…/method_channel/method_channel_firebase.dart:113
E/flutter ( 7724): <asynchronous suspension>
E/flutter ( 7724): #1      Firebase.initializeApp
package:firebase_core/src/firebase.dart:40
E/flutter ( 7724): <asynchronous suspension>
E/flutter ( 7724): #2      _MyHomePageState._asyncInit
package:mobile_app/main.dart:53
E/flutter ( 7724): <asynchronous suspension>
E/flutter ( 7724):

My current app has AppConfig class that extends InheritedWidget that keeps instance available when in use.

You can try with

class AppConfig extends InheritedWidget {

    static AppConfig? of(BuildContext context) {
        initializeFirebase();
        return context.dependOnInheritedWidgetOfExactType<AppConfig>();
      }
    
    static Future<void> initializeFirebase() async {
        try {
          await Firebase.initializeApp();
        } catch (e) {}
      }
}

You need to initialise the AppConfig() in main()

  1. Try to change localhost to your machine ip, in both firebase command line and your app. Some simulators, such as ios simulator, cannot understand localhost.
  2. Please paste a fully reproducible sample, instead of just part of the lines.
  3. Try to await Firebase.initializeApp(); inside main function, not initState
  4. Firstly check at port 8080 manually and see whether the firebase server really works

Technically, the problem is not with the emulator. There are two problems with the code here.

Problem 1: Incorrect connection to the emulator

Providing 'localhost:8080' to the host argument for Firestore Settings is not asking FlutterFire to use the emulator. The right way to use the emulator for Firestore is

FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);

You could wrap that call in an if statement checked against !kReleaseMode so that you won't need to comment it out when you want to build the application. Remember to import flutter/foundation.dart .

Problem 2: No FirebaseOptions provided

The following error:

E/flutter ( 7724): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: [core/not-initialized] Firebase has not been correctly initialized.

is caused from your Firebase.initializeApp() statement.

Firebase.initializeApp() takes an optional named options argument whose value is a type of FirebaseOptions (with parameters like apiKey , appId , projectId , ...), and you are not providing it.

The FirebaseOptions for each platform (android, ios, and web) are available in the Firebase console of a given project. Firebase can't be initialized for any client without its own config or FirebaseOptions .

To setup Firebase in a Flutter project, you need to run the flutterfire configure command in that flutter project folder. The command will ask you to choose a project from the Firebase console. The command will then fetch configs or FirebaseOptions for each platform and make them available through some switch statements for the currentPlatform in a firebase_options.dart file.

You then have to import this file in main.dart and call initializeApp with the option for the given platform as according to the docs :

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

Note that the command can also create apps for you in the Firebase project, if you haven't yet created them.

Let's dive deeper

The options parameter is optional, but it is technically required. If you look at the source code for Firebase.initializeApp() , you will see that under the hood, it invokes MethodChannel calls for the underlying platform (android, ios, or web) on which Flutter is running. So, if this MethodChannel is called without options , some coreNotInitialized() is thrown.

The following is from here

// If there is no native default app and the user didn't provide options to
// create one, throw.
if (defaultApp == null && _options == null) {
  throw coreNotInitialized();
}

This snippet is from initializeApp method in method_channel_firebase.dart file.

There is a conditional block two steps before this one that checks if the current platform is android and if there is a default firebase app available from native android resources. This probably explains why options (as well as name ) are optional at the high-level exposed package.

Thinking deeper, it makes sense that you must provide FirebaseOptions to Firebase.initializeApp() . If you were coding for a specific platform and you were to integrate Firebase or use any of its services, you have to provide those options in one way or the other. In fact, with Flutter, in the past, when the flutterfire command was not yet available, setting up Firebase for Flutter had to be done manually for each platform (in its own folder).

Now that Dart-only Firebase setup is available in the main.dart file directly, it still requires that the FirebaseOptions for each platform is provided. And that is what the flutterfire CLI does during the setup process. It fetches the configs for each platform and imports them. In fact, the docs also specify that you should run flutterfire configure after adding a specific Firebase product plugin .

Solution

So it is clear that to work with any client, you must properly connect a Firebase project from the Firebase console, because the client needs the project's config or FirebaseOptions .

Since you want to work with demo stuff, use the "explore a demo project" feature in the firebase console. This creates a valid temporary Firebase project.

Then when you run the flutterfire configure command, you select this demo project and use it instead.

Furthermore, the Firebase emulators don't use production resources. So as you start the emulators with --only firestore , firestore calls will all be done locally. That said, if you configure FlutterFire with a valid Firebase project, and you use the firestore emulator, you have nothing to fear with regards to interacting with production APIs.

Note that you can use the console demo project with whatever emulator for Firebase service. But remember that these services like firestore for example, are not enabled in the demo project in itself, so if you try to test code against production firestore for the demo project, it won't work.

Also, the demo project in the Firebase console has to be in view while you work with it. There is an "Exit Project" button up top. Once you exist, you've lost the demo project and may have to click on "explore a demo project" again from the home screen to access another demo project

Recap

So ensure your main method looks like this:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
  runApp(const MyApp());
}

And even if you are using a demo emulator, it should now work as expected.

inside main() you can write like this for initilize :

void main() async { 
   WidgetsFlutterBinding.ensureInitialized(); 
   await Firebase.initializeApp();
}

and other thing you have to write :

inside android/app/build.gradle file at the bottom

apply plugin: 'com.google.gms.google-services'

and try this.

[EDIT]

you can check this github post as issue is accepted from flutter as well : https://github.com/firebase/flutterfire/issues/8898

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM