首页 > 解决方案 > 当未加载/被广告拦截器阻止时,firebase_analytics 使 Flutter Web 应用程序崩溃

问题描述

当用户启用了阻止firebase-analytics.js. 我只剩下一个空白页。

这是我收到的错误:

top_level.dart.lib.js:110 Uncaught (in promise) TypeError: dart.global.firebase.analytics is not a function
    at Object.analytics$ [as analytics] (top_level.dart.lib.js:110)
    at new firebase_analytics_web.FirebaseAnalyticsWeb.new (:7357/packages/firebase_analytics_web/firebase_analytics_web.dart.lib.js:56)
    at Function.registerWith (:7357/packages/firebase_analytics_web/firebase_analytics_web.dart.lib.js:19)
    at Object.registerPlugins (:7357/packages/triage/generated_plugin_registrant.dart.lib.js:13)
    at main (:7357/web_entrypoint.dart.lib.js:29)

我正在使用以下版本的firebase_analytics插件

firebase_analytics: ^7.1.1

标签: javascripthtmlflutterdartflutter-web

解决方案


问题

发生这种情况是因为firebase(由 使用firebase_analytics_web)依赖于 JSfirebase.analytics()中存在的函数。请参阅相关的代码。 因此,当广告拦截器阻止加载时,这段代码会抛出,进而导致整个 Flutter Web 初始化崩溃。
firebase-analytics.js

解决方案

我们可以创建一个mockfirebase.analytics()解决这个问题。请注意,在 中firebase_analytics_webfirebase.analytics()函数仅被调用一次,然后存储在一个实例中。这就是为什么我们的模拟必须确保以某种方式firebase.analytics()再次调用 JS,如果我们希望能够firebase-analytics.js在稍后的时间点加载到库中。

这是一个脚本,您<body>至少可以在加载后插入firebase-app.js。例如,如果您遵守 GDPR 并异步加载 Firebase 分析(例如在用户通过 Google 跟踪代码管理器同意之后),这将非常有用。无论如何,这是一个完全解决问题允许稍后加载 Firebase Analytics 的模拟:

<!--
  We need to create a mock for Firebase Analytics that ensures that it *does not matter **when***
  the JS library is loaded. This is because Google Tag Manager will load firebase-analytics.js
  and this 1. happens asynchronously and 2. only after the user consented.
  The firebase.dart Dart library will crash if the firebase.analytics object does not exist,
  which is why this is absolutely crucial for starting the app.
  https://stackoverflow.com/a/66589887/6509751
-->
<script>
  // This mock ensures that if the firebase_analytics Flutter plugin uses this mock as its
  // instance (which does not change over time), the plugin will *still* be able to reach out
  // to the actual firebase.analytics() instance because the object will be overridden once the
  // firebase-analytics.js library is loaded in.
  firebase.analytics = function () {
    return {
      mock: true,
      app: function () {
        var instance = firebase.analytics()
        // Prevent infinite recursion if the real instance has not yet been loaded.
        if (instance.mock === true) return
        return instance.app
      },
      logEvent: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.logEvent(...arguments)
      },
      setAnalyticsCollectionEnabled: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setAnalyticsCollectionEnabled(...arguments)
      },
      setCurrentScreen: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setCurrentScreen(...arguments)
      },
      setUserId: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setUserId(...arguments)
      },
      setUserProperties: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setUserProperties(...arguments)
      },
    }
  }
</script>

推荐阅读