【问题标题】:Flutter Admob's widget get error when setState is call调用 setState 时 Flutter Admob 的小部件出错
【发布时间】:2021-06-13 02:32:57
【问题描述】:

我对 Admob 的小部件有疑问。 我正在为包含 Admob 横幅小部件的颤振应用程序开发一项新功能。 但是当我 setState 另一个小部件的值时,Admob 小部件会出错。

我正在使用:google_mobile_ads: ^0.11.0+1

横幅是这样构建的:

      @override
      void initState() {
        setState(() {
          _adBanner = createBannerAd();
        });
        super.initState();
      }

      @override
      void dispose() {
        _adBanner.dispose();
        super.dispose();
      }

小部件显示如下:

    Container(
        margin: EdgeInsets.only(bottom: myPercent(2, screenHeight)),
        child: FutureBuilder(
            future: _adBanner.load(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return Container(
                  margin: EdgeInsets.only(bottom: 3),
                  width: myPercent(95, screenWidth),
                  height: myPercent(6, screenHeight),
                  alignment: Alignment.center,
                  child: AdWidget(
                    ad: _adBanner,
                  ),
                );
              }
              return Container();
            }),

日志错误捕获:

    flutter: click
    flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY   ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building AdWidget(dirty, state: _AdWidgetState#a1afb):
flutter: This AdWidget is already in the Widget tree
flutter: If you placed this AdWidget in a list, make sure you create a new instance in the builder function
flutter: with a unique ad object.
flutter: Make sure you are not using the same ad object in more than one AdWidget.
flutter:
flutter: The relevant error-causing widget was:
flutter:   AdWidget file:///Users/sofian/Work/Personal/Mobile/WhatUDo/what_u_do/lib/views/idea.dart:295:34
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      _AdWidgetState.build (package:google_mobile_ads/src/ad_containers.dart:372:7)
flutter: #1      StatefulElement.build (package:flutter/src/widgets/framework.dart:4825:27)
flutter: #2      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4708:15)
flutter: #3      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
flutter: #4      BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
flutter: #5      Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
flutter: #6      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4687:5)
flutter: #7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4871:11)
flutter: #8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4682:5)
flutter: ...     Normal element mounting (10 frames)
flutter: #18     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3660:14)
flutter: #19     Element.updateChild (package:flutter/src/widgets/framework.dart:3422:20)
flutter: #20     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
flutter: #21     BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
flutter: #22     Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
flutter: #23     StatelessElement.update (package:flutter/src/widgets/framework.dart:4789:5)
flutter: #24     Element.updateChild (package:flutter/src/widgets/framework.dart:3412:15)
flutter: #25     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4733:16)
flutter: #26     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4880:11)
flutter: #27     BuildOwner._runWithCurrentBuildTarget (package:flutter/src/widgets/framework.dart:2708:15)
flutter: #28     Element.rebuild (package:flutter/src/widgets/framework.dart:4407:12)
flutter: #29     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2813:33)
flutter: #30     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:899:21)
flutter: #31     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:320:5)
flutter: #32     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1119:15)
flutter: #33     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1057:9)
flutter: #34     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:973:5)
flutter: #38     _invoke (dart:ui/hooks.dart:157:10)
flutter: #39     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:253:5)
flutter: #40     _drawFrame (dart:ui/hooks.dart:120:31)
flutter: (elided 3 frames from dart:async)

【问题讨论】:

  • 能否请您添加错误日志?那里似乎有一些信息,但无法在 gif 上阅读..
  • 能否请您链接您使用的广告包? pub.dev 上有一堆 (pub.dev/packages?q=admob)
  • 我也收到同样的错误信息。
  • 嘿,嗨。你能解决这个问题吗?我在这里stackoverflow.com/questions/69412225/… 遇到了同样的问题,而蚂蚁在这里或其他任何地方都找不到任何解决方案。如果你做了,你能告诉一点吗?因为这个问题是我的应用发布前的最后一步!!

标签: flutter set admob state


【解决方案1】:

从问题和错误日志来看,您似乎正在尝试使用 setState 刷新屏幕/小部件,但不是基于实际广告的容器小部件 - 您仍然收到此错误。

如果我理解正确.. 那么问题是关于广告小部件正在尝试使用旧广告对象在 setState 调用上重建,但它期望每次新构建时都有新的广告对象。因此,如果不需要,请避免构建此类小部件。

创建一个单独的基于广告的容器小部件,例如 AppXyzAdWidget,并将广告的父容器代码和其他广告相关代码移动到新小部件中,并在您的屏幕上使用新创建的基于广告的小部件。

通过这种方式,您可以在没有广告相关内容的情况下解耦屏幕,然后setState 不会重新加载您的广告,并且它们的小部件只会刷新您的内容。

【讨论】:

    【解决方案2】:

    调用setState() 会导致小部件重建,因此您的FutureBuilder() 小部件会再次触发未来任务。这就是您看到此错误的原因。

    您需要删除FutureBuilder() 并将您未来的任务移至initState()

    // ...
    // Some code
    
    @override
    void initState() {
      setState(() {
        _adBanner = createBannerAd();
      });
    
      _adBanner.load().whenComplete(() {
        if (this.mounted) {
          setState(() {
            _showAdBanner = true;
          });
        }
      });
      super.initState();
    }
    
    @override
    void dispose() {
      _adBanner.dispose();
      super.dispose();
    }
    
    // ...
    // Some code
    

    您还可以使用布尔值使AdWidget() 在未来任务完成时出现:

    bool _showAdBanner = false;
    
    // ...
    // Some code
    
    _showAdBanner
      ? Container(
      margin: EdgeInsets.only(bottom: 3),
      width: myPercent(95, screenWidth),
      height: myPercent(6, screenHeight),
      alignment: Alignment.center,
      child: AdWidget(
        ad: _adBanner,
      ),
    )
      : Container()
    
    // ...
    // Some code
    

    【讨论】:

      【解决方案3】:

      你能像这样在你的构建函数中删除initState代码并初始化_adBanner吗?

      @override
        Widget build(BuildContext context) {
          _adBanner = createBannerAd();
          return Container(
          margin: EdgeInsets.only(bottom: myPercent(2, screenHeight)),
          child: FutureBuilder(
              future: _adBanner.load(),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  return Container(
                    margin: EdgeInsets.only(bottom: 3),
                    width: myPercent(95, screenWidth),
                    height: myPercent(6, screenHeight),
                    alignment: Alignment.center,
                    child: AdWidget(
                      ad: _adBanner,
                    ),
                  );
                }
                return Container();
              });
        }
      

      【讨论】:

      • 不要在build方法里面放任何加载的东西
      • @NimrSawafta _adBanner.load() 是一个未来,在 FutuerBuilder 中。在 FutureBuilder 中加载未来有什么问题?
      • 是的,每次调用 AdWidget 时,它本身就是一个 Future ,并可能从谷歌暂停。
      • @NimrSawafta 错误说 =>“这个 AdWidget 已经在 Widget 树中颤动:如果你把这个 AdWidget 放在一个列表中,请确保你在构建器函数颤振中创建一个新实例:使用唯一的广告对象”。所以每次都需要加载广告来解决问题。然而,admob 包已经更新了处理这个问题。
      猜你喜欢
      • 2019-03-16
      • 1970-01-01
      • 1970-01-01
      • 2018-01-06
      • 2021-05-01
      • 2020-08-18
      • 1970-01-01
      • 2020-11-26
      • 1970-01-01
      相关资源
      最近更新 更多