web-dev-qa-db-ja.com

WebのFlutter firebaseリアルタイムデータベース

フラッターアプリをブラウザーで機能させようとしていますが、Firebase_databaseに依存しています。これを行うためのドキュメントは実際にはありませんが、firebase_coreとfirebase_authのドキュメントに基づいていくつかの仮定を行っています:

アプリはiOSとAndroidで動作していますが、フラッターWebでデータベースを動作させるのに問題があります。

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>

しかし、firebaseデータベースを使用しようとすると、ログにエラーが記録されます。

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>

Web上のFlutterアプリでリアルタイムデータベースを機能させる方法はありますか?

5
Corey Cole

この問題に数日間苦労した後、コメントに未回答の質問がほとんどないので、私は完全なドウの長い投稿をすることにしました。これは、2つの異なるパッケージを実装する方法です。状態管理に_flutter_bloc_を使用しているため、ユーザーの場所の場合と同様に、基本的にリポジトリプラットフォームを依存させる必要がありました。それを実現するには、スタブ/抽象クラス/ Web実装/デバイス実装パターンを使用します。私のブロックのリポジトリでは、適切なパッケージを使用して適切なプラットフォーム実装クラスにマップする抽象クラスメソッドを呼び出すだけです。最初は少し厄介に見えますが、コンセプトを理解すれば非常に簡単ですが、Theraは、パターンを開始するときに陥りがちないくつかの罠です。デバイスの実装では_flutter_auth_パッケージが使用されますが、Webの実装ではflutterパッケージが代わりに使用され、簡単にするためにシングルトンを作成しました。シングルトンが初期化されたfirebase Appを返すようになり、すべてのサービスにアクセスできるようになりました。_auth()database()、firestore() `、remoteconfig()... Firebaseをインスタンス化してサービスを使用するだけで、Firebaseサービスにアクセスする必要があります。

_App firebase = FirebaseWeb.instance.app;

...

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

承認に使用するすべてのコードは次のとおりですが、さまざまなFirebaseサービスに簡単に適応できます。

スタブ:

これは、抽象クラスファクトリメソッド(スイッチャーと呼びます)で返される(getter)メソッドを保持し、抽象クラスで適切な実装クラスへの条件付きインポートを可能にするためです。

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

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

抽象クラス(スイッチャー):

ここでは、適切な実装クラスを条件付きでインポートできるようにスタブをインポートします。クラスファクトリメソッドで返されるスタブ(getter)メソッド。このクラスでは、使用する必要があるすべてのメソッドを宣言する必要があります。パッケージ固有の返品はプラットフォーム実装クラスに含まれるため、返品は動的です。自動チェックがないので、条件付きインポートでタイプミスと適切なファイルルートに注意してください。それを見つけるのに多くのコストがかかりました。

_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();
}

_

デバイス実装クラス:

抽象クラスを実装して、特定の(この場合は_flutter_auth_)メソッドと型を持つメソッドを取得して実装する必要があります。ここでは、クラスのスコープの外で、デバイス実装クラスを返す同じメソッドをスタブで宣言する必要もあります(下のコードを参照)。

_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();

_

いよいよウェブ版です。

firebaseシングルトン:

firebaseパッケージを簡単な方法で使用するために、それをシングルトンにすることにしました。ここでは、_Future<App>_インスタンスを返すことができますが、_.then_すべてを返すか、Appを直接返す必要があります。この方法を選択しました。よりクリーンで迅速な実装。この方法では、_index.html_ファイルでfirebaseを初期化する必要はありません。そうしないと、すでに初期化されているためエラーが発生します。ここでFirebaseを初期化すると、キーが公開されなくなります。

_
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;
    }
  }
}
_

Web実装:

ここでは、シングルトンを使用してFirebaseをインスタンス化し、抽象クラスメソッドを実装し、そのサービスとメソッドを使用します。ここではauth()を使用します。シングルトンで_Future<App>_を返す場合、実装がどれだけ冗長であるか(コメントアウトされた部分)を確認できます。ここで、スタブゲッターメソッドはこのクラスを返します..(下をチェック)

_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