网络中的Flutter Firebase实时数据库

时间:2020-01-09 19:18:52

标签: firebase flutter firebase-realtime-database flutter-web

我正在尝试让我的flutter应用程序在浏览器中运行,这取决于firebase_database。确实没有任何有关此操作的文档,但是我根据firebase_core和firebase_auth文档进行了一些假设:

我的应用程序可以在iOS和android上运行,但是我无法使数据库在波动的网络中正常工作。

我已经设置了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 个答案:

答案 0 :(得分:12)

the main README in the flutterfire github中,有一个“ Web”?列,以说明哪些插件可用于网络。

当前,浮动Web应用程序仅支持firebase_corefirebase_authcloud_firestorefirebase_functions

如@Frank van Puffelen所述,要在flutter网站中使用firebase的全部功能,请使用firebase-dart包装器库。

还有一个Flutter Web Plugins Project Board,显示了路线图上哪些flutter插件以及它们处于开发的哪个阶段。在进行此编辑时,firebase_storage是网络路线图上的下一个插件。

答案 1 :(得分:5)

FlutterFire插件最初是为在iOS和Android的本机移动应用程序中运行而构建的。正如我们所说的那样,对Web平台的支持正在添加到插件中,但是涵盖所有Firebase产品还需要一些时间。

您可以在Github存储库上的available FlutterFire plugins列表中检查当前与Flutter for web兼容的模块。

要在Flutter中将Firebase用于其他功能的网络,请使用firebase-dart plugin。这意味着您将需要针对Web和移动设备使用单独的代码,但是您可以仅在应用程序的一小部分中就可以区分出差异。

答案 2 :(得分:2)

我遇到了同样的问题,因此决定对此做些事情。所以我继续做https://pub.dev/packages/firebase_db_web_unofficial。设置并集成到您的代码中很容易。

答案 3 :(得分:2)

以防万一有人仍在寻找 Flutter Web 实时数据库问题的另一种解决方法,我有一个简单而直接的解决方案......

我做了一些挖掘,if(kIsWeb) 似乎有效。

第一

添加支持网络实时数据库的firebase package和安卓|ios的firebase_databe package

第二

初始化firebase

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

第三

导入如下

import 'package:firebase_database/firebase_database.dart';
import 'package:firebase/firebase.dart' as fb;

第四

关于如何读取 android-ios / web 实时数据库数据的示例。 在这里,我将图像添加到轮播滑块。

List<SliderImage> sliderList = [];

void getSliderData() async {
FirebaseDatabase firebaseDatabaseference = FirebaseDatabase.instance;
firebaseDatabaseference.setPersistenceEnabled(true);
firebaseDatabaseference.setPersistenceCacheSizeBytes(10000000);



//for web
if (kIsWeb) {
  fb.DatabaseReference databaseRef = fb.database().ref("Slider");
  await databaseRef.onValue.listen((event) {
    fb.DataSnapshot dataSnapshot = event.snapshot;
    sliderList.clear();
    this.setState(() {
      for (var value in dataSnapshot.val()) {
        sliderList.add(new SliderImage.fromJson(value));
      }
    });
  });
  // for android and ios
} else {
  DatabaseReference databaseReference = firebaseDatabaseference.reference();

  databaseReference.keepSynced(true);
  await databaseReference
      .child("Slider")
      .once()
      .then((DataSnapshot dataSnapshot) {
    sliderList.clear();
    this.setState(() {
      for (var value in dataSnapshot.value) {
        sliderList.add(new SliderImage.fromJson(value));
      }
    });
  });
}

}

轮播滑块

CarouselSlider.builder(
      itemCount: sliderList.length,
      options: CarouselOptions(
        autoPlay: true,
        aspectRatio: 16 / 9,
        viewportFraction: 1,
        enlargeCenterPage: false,
        enlargeStrategy: CenterPageEnlargeStrategy.height,
      ),
      itemBuilder: (context, index, realIdx) {
        return Container(
          child: Center(
              child: Image.network(sliderList[index].image, loadingBuilder:
                  (BuildContext context, Widget child,
                      ImageChunkEvent loadingProgress) {
            if (loadingProgress == null) return child;
            return Center(
              child: CircularProgressIndicator(
                valueColor:
                    new AlwaysStoppedAnimation<Color>(Colors.black54),
                value: loadingProgress.expectedTotalBytes != null
                    ? loadingProgress.cumulativeBytesLoaded /
                        loadingProgress.expectedTotalBytes
                    : null,
              ),
            );
          }, fit: BoxFit.cover, width: 1000)),
        );
      },
    ));

SliderImage 模型类

class SliderImage {
 String image;

 SliderImage(this.image);

 SliderImage.fromJson(var value) {
   this.image = value['image'];
 }
}

类似的方法可以应用于 Listview.builder。 干杯????????

答案 4 :(得分:0)

在这个问题上经过几天的努力之后,由于评论中未解决的问题很少,我决定发布一个完整的,长篇幅的答案,以帮助人们从我本人的角度出发。 这就是我实现两个不同程序包的方式。 当我使用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)方法,该方法在抽象类工厂方法(我称为switcher)中返回,并允许将抽象类中的条件导入到正确的实现类。

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

网络实施:

在这里,您仅使用单例实例化Firebase,并实现抽象类方法,使用其服务和方法。我在这里使用auth()。 您可以看到(注释掉的部分)如果在单例中返回Future<App>,则实现的详细程度要高得多。 在这里,存根getter方法将返回此类..(检查底部)

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