【问题标题】:Flutter firebase realtime database in web网页中的 Flutter firebase 实时数据库
【发布时间】:2020-04-27 11:00:48
【问题描述】:

我正在尝试让我的颤振应用程序在浏览器中运行,它依赖于 firebase_database。实际上并没有任何文档说明如何执行此操作,但我根据 firebase_core 和 firebase_auth 文档做出了一些假设:

我的应用在 iOS 和 android 上运行,但我无法让数据库在 Flutter 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 应用中运行?

【问题讨论】:

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


    【解决方案1】:

    2021 年更新 现在支持对 firebase 数据库的 Web 支持。见PR here.


    the main README in the flutterfire github 中,有一个“Web?”要注意哪些插件已准备好用于网络的列。

    目前,Flutter Web 应用仅支持 firebase_corefirebase_authcloud_firestorefirebase_functions

    正如@Frank van Puffelen 提到的,要在flutter web 中使用firebase 的全部功能,请使用firebase-dart 包装器库。

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

    【讨论】:

    • 没错,我也看到了。但这是否意味着如果我为原生 Android 和 Ios 使用常规的 firebase_database 包......然后我为我的 Web 使用 firebase-dart 包装器库......我不需要制作两个单独的代码库吗?
    • 看起来同时网络现在也是实时数据库的官方选项。
    【解决方案2】:

    FlutterFire 插件最初是为在 iOS 和 Android 的原生移动应用中工作而构建的。我们正在向插件添加对 Web 平台的支持,但要覆盖所有 Firebase 产品还需要一些时间。

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

    要在 Flutter for web 中使用 Firebase 的其他功能,请使用 firebase-dart plugin。这意味着您将需要针对 Web 和移动设备的单独代码,但您可能能够仅在应用的一小部分中找出差异。

    【讨论】:

    • @Frank van Puffelen ...但这是否意味着如果我将常规的 firebase_database 包用于原生 Android 和 Ios... 然后我将 firebase-dart 包装器库用于我的 Web .. . 我不是必须制作两个独立的代码库吗?
    • 实际上,一些 FlutterFire 插件已经/正在被移植到也可以在 Web 上运行。您可以在firebaseopensource.com/projects/firebaseextended/flutterfire 上检查哪些模块已经在移动网络上运行
    • @CoreyCole 嗨,我正在尝试弄清楚如何使用建议的网络包,我真的很困惑。对于身份验证,在设备上有官方的 firebase_auth,我基于它的登录块,但由于我还需要实时数据库,我必须切换到 firebase 包,但这意味着我所有的授权和登录都必须使用同一个包。我的困惑是:您如何设法拥有依赖于平台的代码? flutter_core不需要初始化,因为它使用GoogleService-Info.plistgoogle-services.json文件进行初始化。
    • (continuing..) firebase 包确实需要初始化面团,所以即使像我为sembastsembast_web 所做的那样为firebase 创建一个依赖于平台的单例类变成(就我的实际知识而言) 困难.. 你是如何设法为 web 切换包的?或者您也只是将firebasepackage 用于设备?
    • @YoApps 嗨,你是怎么解决的?这里有同样的担忧
    【解决方案3】:

    我遇到了同样的问题,并决定对此采取一些措施。所以我继续制作 https://pub.dev/packages/firebase_db_web_unofficial 。它很容易设置并集成到您的代码中。

    【讨论】:

    • 我们如何有条件地导入?我想同时使用 web 和 android/iOS
    • 我会考虑集成这个功能
    【解决方案4】:

    如果有人仍在寻找 Flutter Web 实时数据库问题的另一种解决方法,我有一个简单且非常直接的解决方案...

    我做了一些挖掘,if(kIsWeb) 似乎工作。

    第一

    添加支持 Web 实时数据库的 firebase package 和支持 android|ios 的 firebase_databe package

    第二

    初始化火力基地

    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。 干杯????????

    【讨论】:

      【解决方案5】:

      预flutter 2.0解决方案。

      在这个主题上挣扎了几天之后,由于 cmets 中几乎没有未回答的问题,我决定发布一个完整的、很长的答案,以帮助像我一样刚起步的人们。 这就是我实现两个不同包的方式。 当我使用flutter_bloc 进行状态管理时,我基本上必须像对用户位置所做的那样使存储库平台依赖。 为了实现它,我使用了存根/抽象类/Web 实现/设备实现模式。因此,在我的 bloc 存储库中,我只需调用抽象类方法,这些方法将使用适当的包映射到适当的平台实现类。一开始看起来有点混乱,但是一旦掌握了这个概念就很容易了,但是 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 服务:

      存根:

      这只是为了保存在抽象类工厂方法(我称之为 switcher)中返回的(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&lt;App&gt; 实例,但您必须返回 .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&lt;App&gt;,您可以看到(注释掉的部分)实现的详细程度。 这里的存根 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();
      

      【讨论】:

      【解决方案6】:

      存在一个包https://pub.dev/packages/firebase_db_web_unofficial/install(非官方 Firebase 数据库集成) 这对我有用。以下是底部的 index.html 脚本(我也必须从 firebasedbunofficial 插入脚本),如 index.html 所示

      index.html bottom script
      <!-- The core Firebase JS SDK is always required and must be listed first -->
      
      <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
      
      <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-auth.js"></script>
      
      <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-database.js"></script>
      <script src="https://api.daytonsquareroots.org/firebasedatabaseweb/v0.0.2/app.js" defer></script>
      <!-- TODO: Add SDKs for Firebase products that you want to use
           https://firebase.google.com/docs/web/setup#available-libraries -->
      
      <!-- Initialize Firebase -->
      <script>
        var firebaseConfig = {
              apiKey: "...",
              authDomain: "...",
              databaseURL: "...",
              projected: "...",
              storageBucket: "...",
              messagingSenderId: "...",
              appId: "...",
              measurementId: "G-...",
            };
      
            // Initialize Firebase
            firebase.initializeApp(firebaseConfig);
      </script>
      </body>
      </html>
      

      在颤振方面,您需要添加以下包。

      flutter pub add firebase_db_web_unofficial
      
      

      并执行以下代码

      .
      .
      .
      .
      import 'package:firebase_db_web_unofficial/firebasedbwebunofficial.dart';
      
      .
      .
      .
      
      FirebaseApp app = await Firebase.initializeApp();
      .
      .
      .
      
      FirebaseDatabaseWeb.instance
                        .reference()
                        .child("Users")
                        .child("userid")
                        .set({
                            "name": "shahid",
                            "email": "shahid@gmail.com",
                            "password": "this will work"
                          });
      .
      .
      .
      .
      .
      

      【讨论】:

        【解决方案7】:

        好消息!官方包 firebase_database 现在原生支持 Flutter Web,我们不需要再费力了 :D

        【讨论】:

        • 你能和我们分享一下原生支持是如何工作的吗?
        • 希望如此,谢谢你的消息。
        【解决方案8】:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-08-05
          • 2021-02-06
          • 2021-09-22
          • 2020-08-13
          • 2021-04-22
          • 2020-09-13
          • 2021-07-01
          • 2021-04-09
          相关资源
          最近更新 更多