web-dev-qa-db-fra.com

Base de données en temps réel Flutter Firebase sur le Web

J'essaie de faire fonctionner mon application Flutter dans le navigateur et cela dépend de firebase_database. Il n'y a pas vraiment de documentation sur la façon de faire cela, mais je fais des hypothèses basées sur les documentations firebase_core et firebase_auth:

Mon application fonctionne sur iOS et Android, mais j'ai du mal à faire fonctionner la base de données dans Flutter Web.

J'ai configuré mon index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flutter WebRTC Demo</title>
</head>
<body>
    <script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-database.js"></script>
    <script>
        const firebaseConfig = {
            apiKey: '...',
            authDomain: '...',
            databaseURL: '...',
            projectId: '...',
            storageBucket: '...',
            messagingSenderId: '...',
            appId: '...'
        };
        firebase.initializeApp(firebaseConfig);
    </script>
    <script src="main.Dart.js" type="application/javascript"></script>
</body>
</html>

Mais, lorsque j'essaie d'utiliser la base de données Firebase, j'obtiens des erreurs dans les journaux:

MissingPluginException(No implementation found for method DatabaseReference#set on channel plugins.flutter.io/firebase_database)
package:Dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.Dart 196:49  throw_
package:flutter/src/services/platform_channel.Dart 319:7                              invokeMethod
package:Dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.Dart 47:50            onValue
package:Dart-sdk/lib/async/zone.Dart 1381:54                                          runUnary
package:Dart-sdk/lib/async/future_impl.Dart 139:18                                    handleValue
package:Dart-sdk/lib/async/future_impl.Dart 680:44                                    handleValueCallback
package:Dart-sdk/lib/async/future_impl.Dart 709:32                                    _propagateToListeners
package:Dart-sdk/lib/async/future_impl.Dart 524:5                                     [_completeWithValue]
package:Dart-sdk/lib/async/future_impl.Dart 554:7                                     callback
package:Dart-sdk/lib/async/schedule_microtask.Dart 43:11                              _microtaskLoop
package:Dart-sdk/lib/async/schedule_microtask.Dart 52:5                               _startMicrotaskLoop
package:Dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.Dart 168:15           <fn>

Existe-t-il un moyen de faire fonctionner la base de données en temps réel dans mon application Flutter sur le Web?

5
Corey Cole

Après quelques jours de lutte sur ce sujet et comme il y a peu de questions sans réponse dans les commentaires, je décide de publier une réponse complète et longue pour aider les gens qui commencent tout juste à flotter comme moi. C'est ainsi que j'implémente les deux packages différents. Comme j'utilise flutter_bloc Pour la gestion de l'état, j'ai dû rendre la plate-forme de dépôt dépendante comme je l'ai fait pour l'emplacement de l'utilisateur. Pour y parvenir, j'utilise un modèle d'implémentation stub/classe abstraite/implémentation web/appareil. Donc, dans le référentiel de mon bloc, j'appelle simplement les méthodes de classe abstraites qui mapperont à la classe d'implémentation de plate-forme appropriée en utilisant le package approprié. Cela semble un peu compliqué au début, mais c'est assez facile une fois le concept compris, mais Thera est un couple de pièges dans lesquels on pourrait tomber en commençant par le modèle. Pour l'implémentation du périphérique, le package flutter_auth Est utilisé tandis que pour l'implémentation Web, le package flutter est utilisé à la place et pour faciliter les choses, j'ai créé un singleton. Maintenant, le singleton renvoie la base de feu initialisée App qui vous donne accès à tous les services .. auth(), database(), firestore () `, remoteconfig () ... Partout vous devez accéder à n'importe quel service Firebase, simplement instancier Firebase et utiliser les services.

App firebase = FirebaseWeb.instance.app;

...

await firebase.auth().signInWithCredential(credential);
    return firebase.auth().currentUser;

Voici donc tout le code que j'utilise pour l'autorisation, mais il est facile à adapter pour différents services Firebase:

Bout:

c'est juste pour contenir une méthode (getter) qui est retournée dans la méthode de fabrique de classe abstraite (je l'appelle switcher), et pour permettre l'importation conditionnelle dans la classe abstraite vers la classe d'implémentation appropriée.

import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.Dart';

UserRepositorySwitcher getUserRepository() {
  print('user_repository_stub called');
}

Classe abstraite (le mélangeur):

Ici, vous importez le stub pour pouvoir importer conditionnellement la classe d'implémentation appropriée. La méthode stub (getter) est renvoyée dans la méthode de fabrique de classes. Dans cette classe, vous devez déclarer toutes les méthodes que vous devez utiliser. Ici, les retours sont dynamiques car les retours spécifiques au package seront dans les classes d'implémentation de la plateforme. Méfiez-vous des fautes de frappe et des chemins de fichiers appropriés dans l'importation conditionnelle car il n'y a pas de vérification automatique ... m'a coûté beaucoup de nôtres pour le découvrir hahah ..

import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_stub.Dart'
    if (Dart.library.io) 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_device.Dart'
    if (Dart.library.js) 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_web.Dart';

abstract class UserRepositorySwitcher {
  Future<dynamic> signInWithGoogle() async {
    print('UserREpository switcher signInWithGoogle() called');
  }

  Future<void> signInWithCredential({String email, String password}) {}
  Future<void> signUp({String email, String password}) {}
  Future<void> signOut() async {}
  Future<bool> isSignedIn() async {}
  Future<dynamic> getUser() async {}

  factory UserRepositorySwitcher() => getUserRepository();
}

Classe d'implémentation de l'appareil:

Doit implémenter une classe abstraite pour obtenir et implémenter ses méthodes avec des méthodes et des types spécifiques (flutter_auth Dans ce cas). Ici, vous devez également déclarer, en dehors de la portée de la classe, la même méthode dans le stub, qui renvoie la classe d'implémentation de l'appareil (voir le code du bas).

import 'package:firebase_auth/firebase_auth.Dart';
import 'package:firebaseblocwebstub/authentication_bloc/app_user.Dart';
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.Dart';
import 'package:google_sign_in/google_sign_in.Dart';

class UserRepositoryDevice implements UserRepositorySwitcher {
  final FirebaseAuth _firebaseAuth;
  final GoogleSignIn _googleSignIn;

  UserRepositoryDevice({FirebaseAuth firebaseAuth, GoogleSignIn googleSignIn})
      : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
        _googleSignIn = googleSignIn ?? GoogleSignIn();

  Future<FirebaseUser> signInWithGoogle() async {
    print('signInWithGoogle() from device started');
    final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
    print('GoogleUser is : $googleUser');
    final GoogleSignInAuthentication googleAuth =
        await googleUser.authentication;
    final AuthCredential credential = await GoogleAuthProvider.getCredential(
        idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
    await _firebaseAuth.signInWithCredential(credential);
    return _firebaseAuth.currentUser();
  }

  Future<void> signInWithCredential({String email, String password}) {
    return _firebaseAuth.signInWithEmailAndPassword(
        email: email, password: password);
  }

  Future<void> signUp({String email, String password}) {
    return _firebaseAuth.createUserWithEmailAndPassword(
        email: email, password: password);
  }

  Future<void> signOut() async {
    return Future.wait([
      _firebaseAuth.signOut(),
      _googleSignIn.signOut(),
    ]);
  }

  Future<bool> isSignedIn() async {
    final currentUser = _firebaseAuth.currentUser();
    return currentUser != null;
  }

  Future<FixitUser> getUser() async {
    String displayName = (await _firebaseAuth.currentUser()).displayName;
    String email = (await _firebaseAuth.currentUser()).email;
    String uid = (await _firebaseAuth.currentUser()).uid;
    String photoUrl = (await _firebaseAuth.currentUser()).photoUrl;
    String phoneNumber = (await _firebaseAuth.currentUser()).phoneNumber;
    FixitUser user = FixitUser(
        // fixitUser
        name: displayName ?? '',
        email: email,
        phoneNumber: phoneNumber ?? '',
        uid: uid,
        photoUrl: photoUrl ?? '');
    return (user);
  }
}

UserRepositorySwitcher getUserRepository() => UserRepositoryDevice();

Maintenant enfin pour le Web.

singleton firebase:

Pour utiliser le package firebase d'une manière simple, j'ai décidé d'en faire un singleton. Ici, vous pouvez soit retourner une instance Future<App> Mais ensuite vous devez .then Tout..ou renvoyer le App directement..J'ai choisi cette façon..implémentation plus propre et plus rapide. De cette façon, vous n'avez pas besoin d'initialiser Firebase dans votre fichier index.html Ou vous obtiendrez une erreur car il est déjà initialisé. Initialiser Firebase ici rend également vos clés non exposées.


import 'Dart:async';
import 'package:firebase/firebase.Dart';

class FirebaseWeb {
  // Singleton instance
  static final FirebaseWeb _singleton = FirebaseWeb._();

  // Singleton accessor
  static FirebaseWeb get instance => _singleton;

  // A private constructor. Allows us to create instances of AppDatabase
  // only from within the AppDatabase class itself.
  FirebaseWeb._();

  static App _app;
  // Database object accessor

  App get app {
    print('firebase get app called ');
    print('_app is $_app');
    if (_app != null) {
      return _app;
    } else {
      print('initialize app');
      _app = initializeApp(
          apiKey: "your key",
          authDomain: "your key",
          databaseURL: "your key",
          projectId: "your key",
          storageBucket: "your key",
          messagingSenderId: "your key",
          appId: "your key");
      print('initialized app is $_app'); // await _initializeApp();
      return _app;
    }
  }
}

Implémentation Web:

Ici, vous instanciez simplement Firebase en utilisant le singleton, et implémentez des méthodes de classe abstraites, utilisez ses services et méthodes..J'utilise auth() ici. Vous pouvez voir (parties commentées) à quel point l'implémentation est plus verbeuse si vous retournez un Future<App> Dans le singleton .. Ici, la méthode getter stub retournera cette classe .. (vérifier en bas)

import 'Dart:async';
import 'package:firebase/firebase.Dart';
import 'package:firebaseblocwebstub/authentication_bloc/app_user.Dart';
import 'package:firebaseblocwebstub/firebase_singleton.Dart';
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.Dart';
import 'package:google_sign_in/google_sign_in.Dart';

class UserRepositoryWeb implements UserRepositorySwitcher {
  App firebase = FirebaseWeb.instance.app;
  final GoogleSignIn _googleSignIn = GoogleSignIn();

  Future<User> signInWithGoogle() async {
    print('signInWithGoogle() started');
    final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
    print('GoogleUser is : $googleUser');
    final GoogleSignInAuthentication googleAuth =
        await googleUser.authentication;
    final OAuthCredential credential = await GoogleAuthProvider.credential(
        googleAuth.idToken, googleAuth.accessToken);
    // singleton retunrning Future<App>
//    await firebase.then((firebase) {
//      firebase.auth().signInWithCredential(credential);
//      return;
//    });
//    return firebase.then((firebase) {
//      return firebase.auth().currentUser;
//    });

    await firebase.auth().signInWithCredential(credential);
    return firebase.auth().currentUser;
  }

  Future<void> signInWithCredential({String email, String password}) {
    return firebase.auth().signInWithEmailAndPassword(email, password);
    // singleton retunrning Future<App>
//    return firebase.then((firebase) {
//      return firebase.auth().signInWithEmailAndPassword(email, password);
//    });
  }

  Future<void> signUp({String email, String password}) {
    return firebase.auth().createUserWithEmailAndPassword(email, password);
    // singleton retunrning Future<App>
//    return firebase.then((firebase) {
//      return firebase.auth().createUserWithEmailAndPassword(email, password);
//    });
  }

  Future<void> signOut() async {
    return Future.wait([
      firebase.auth().signOut(),
// singleton retunrning Future<App>
//      firebase.then((firebase) {
//        firebase.auth().signOut();
//      }),
      _googleSignIn.signOut(),
    ]);
  }

  Future<bool> isSignedIn() async {
    final currentUser = firebase.auth().currentUser;
    return currentUser != null;
    // singleton retunrning Future<App>
//    User firebaseUser = firebase.then((firebase) {
//      return firebase.auth().currentUser;
//    }) as User;
//    return firebaseUser != null;
  }

  Future<FixitUser> getUser() async {
    // singleton retunrning Future<App>
//    User firebaseUser = firebase.then((firebase) {
//      return firebase.auth().currentUser;
//    }) as User;
//
//    FixitUser user = FixitUser(
//        name: firebaseUser.displayName ?? '',
//        email: firebaseUser.email,
//        phoneNumber: firebaseUser.phoneNumber ?? '',
//        uid: firebaseUser.uid,
//        photoUrl: firebaseUser.photoURL ?? '');
//    return (user);
//  }
    String displayName = (firebase.auth().currentUser).displayName;
    String email = (firebase.auth().currentUser).email;
    String uid = (firebase.auth().currentUser).uid;
    String photoUrl = (firebase.auth().currentUser).photoURL;
    String phoneNumber = (firebase.auth().currentUser).phoneNumber;

    FixitUser user = FixitUser(
        name: displayName ?? '',
        email: email,
        phoneNumber: phoneNumber ?? '',
        uid: uid,
        photoUrl: photoUrl ?? '');
    return (user);
  }
}

UserRepositorySwitcher getUserRepository() => UserRepositoryWeb();
0
Vincenzo