Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preloaded banner causes "AdWidget is already in the Widget tree" on first navigation, then OK all next navigations #878

Closed
large opened this issue Jul 5, 2023 · 11 comments
Assignees

Comments

@large
Copy link

large commented Jul 5, 2023

Issue is related to finding a good way to have preloaded banners and possibility to navigate as expected.

Plugin Version

google_mobile_ads: ^3.0.0

Steps to Reproduce

Clone example app: https://github.com/large/admobpreload
This is a quick hack to try to create a preloading of banner (pro example much wanted!) to get a better experience in app.
Reuse will cause 3-6 seconds loading time and user are long gone many times when it is ready.

See example on video
https://github.com/googleads/googleads-mobile-flutter/assets/535446/4c644001-fad2-439d-8986-fa795450c178

Expected results:
Navigator.of(context).pushNamedAndRemoveUntil(...) should give a "clean stack" so you only have one window.
The Adwidget could be reused on each page for minimum loadingtime on a "single window" application without hickups.

Actual results:
First navigation will always give the "This AdWidget is already in the Widget tree", then show as normal on the navigation later on using pushNamedAndRemoveUntil(...)

Backgroundinfo:
Navigator.of(context).pushNamed(¨¨, (Route<dynamic> route) => false) will stack windows and error is "correct" on this (several instances).
The Navigator.of(context).pushReplacementNamed(..) does not remove the the stack only replace the route it is navigating to. So this could also fail as expected. But the

Logs
flutter: ########## BannerAd from impression given.
flutter: Ad was loaded, show it now

======== Exception caught by widgets library =======================================================
The following assertion was thrown building AdWidget(dirty, state: _AdWidgetState#7a7a3):
This AdWidget is already in the Widget tree


If you placed this AdWidget in a list, make sure you create a new instance in the builder function with a unique ad object.
Make sure you are not using the same ad object in more than one AdWidget.

The relevant error-causing widget was: 
  AdWidget AdWidget:file:///Users/large/StudioProjects/AdMobPreload/lib/bannerpreload.dart:113:18
When the exception was thrown, this was the stack: 
#0      _AdWidgetState.build (package:google_mobile_ads/src/ad_containers.dart:674:7)
#1      StatefulElement.build (package:flutter/src/widgets/framework.dart:5198:27)
#2      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5086:15)
#3      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#4      Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#5      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5068:5)
#6      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5242:11)
#7      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5062:5)
...     Normal element mounting (24 frames)
#31     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#32     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6570:36)
#33     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6582:32)
...     Normal element mounting (28 frames)
#61     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#62     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6570:36)
#63     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6582:32)
...     Normal element mounting (136 frames)
#199    Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#200    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6570:36)
#201    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6582:32)
...     Normal element mounting (178 frames)
#379    Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#380    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6570:36)
#381    Element.updateChild (package:flutter/src/widgets/framework.dart:3708:18)
#382    RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:6153:32)
#383    MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6595:17)
#384    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#385    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#386    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#387    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#388    StatefulElement.update (package:flutter/src/widgets/framework.dart:5274:5)
#389    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#390    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#391    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#392    ProxyElement.update (package:flutter/src/widgets/framework.dart:5417:5)
#393    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#394    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#395    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#396    ProxyElement.update (package:flutter/src/widgets/framework.dart:5417:5)
#397    _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:107:11)
#398    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#399    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#400    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#401    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#402    StatefulElement.update (package:flutter/src/widgets/framework.dart:5274:5)
#403    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#404    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#405    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#406    ProxyElement.update (package:flutter/src/widgets/framework.dart:5417:5)
#407    _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:107:11)
#408    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#409    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#410    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#411    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#412    StatefulElement.update (package:flutter/src/widgets/framework.dart:5274:5)
#413    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#414    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#415    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#416    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#417    StatefulElement.update (package:flutter/src/widgets/framework.dart:5274:5)
#418    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#419    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6442:14)
#420    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#421    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6442:14)
#422    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#423    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#424    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#425    ProxyElement.update (package:flutter/src/widgets/framework.dart:5417:5)
#426    Element.updateChild (package:flutter/src/widgets/framework.dart:3686:15)
#427    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#428    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#429    Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#430    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2780:19)
#431    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:903:21)
#432    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:358:5)
#433    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1284:15)
#434    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1214:9)
#435    SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1072:5)
#436    _invoke (dart:ui/hooks.dart:142:13)
#437    PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:359:5)
#438    _drawFrame (dart:ui/hooks.dart:112:31)
====================================================================================================
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
flutter: ########## BannerAd from impression given.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
flutter: ########## BannerAd from impression given.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
flutter: ########## BannerAd from impression given.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
flutter: ########## BannerAd from impression given.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
flutter: ########## BannerAd from impression given.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): BannerPreloadADState#42c63(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widg<…>
Analyzing AdMobPreload...                                               

warning • This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final: AdContainer._adLoaded, AdContainer._banner, AdContainer._inLineCorrected •
       lib/admobpreload.dart:44:7 • must_be_immutable
   info • The file name 'anchored_adaptive_banner_adSize.dart' isn't a snake_case identifier • lib/anchored_adaptive_banner_adSize.dart:1:1 • file_names
warning • Unused import: 'dart:io' • lib/bannerpreload.dart:2:8 • unused_import
   info • Use 'const' with the constructor to improve performance • lib/bannerpreload.dart:125:16 • prefer_const_constructors

4 issues found. (ran in 1.0s)
[✓] Flutter (Channel stable, 3.10.5, on macOS 13.4.1 22F82 darwin-arm64, locale nb-NO)
    • Flutter version 3.10.5 on channel stable at /Users/large/dev/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 796c8ef792 (3 weeks ago), 2023-06-13 15:51:02 -0700
    • Engine revision 45f6e00911
    • Dart version 3.0.5
    • DevTools version 2.23.1

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at /Users/large/Library/Android/sdk
    • Platform android-33, build-tools 33.0.2
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14E300c
    • CocoaPods version 1.12.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2022.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)

[✓] Connected device (4 available)
    • sdk gphone64 arm64 (mobile) • emulator-5554                        • android-arm64  • Android 13 (API 33) (emulator)
    • iPhone 14 Pro Max (mobile)  • AC0DBD9C-84C5-4E2E-B6EC-55D957759413 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-4 (simulator)
    • macOS (desktop)             • macos                                • darwin-arm64   • macOS 13.4.1 22F82 darwin-arm64
    • Chrome (web)                • chrome                               • web-javascript • Google Chrome 114.0.5735.198

[✓] Network resources
    • All expected network resources are available.

• No issues found!

@malandr2 malandr2 added the in triage Issue currently being evaluated label Jul 5, 2023
@malandr2 malandr2 added bug Something isn't working p2-medium labels Jul 6, 2023
@malandr2
Copy link
Collaborator

malandr2 commented Jul 6, 2023

Hi @large,

Thank you for flagging this issue. I am able to replicate with your sample application. I have escalated this for a fix.

@Patelparth2442
Copy link

I'm encountering a similar problem. To illustrate, let's say I have three screens. I'm currently loading banner ads in "bannerAdWidget" and displaying the same ad in all three screens. However, this approach triggers a warning:

Ensure that a single ad object is not utilized across multiple AdWidgets.

Requirement - Is there a solution available at the moment or planned for the near future that would allow for loading ads in one AdWidget and subsequently using them throughout the entire app, essentially attaching and detaching them as needed?

@bruce-yan
Copy link

hi @malandr2,
When will the upgrade take place?

@malandr2
Copy link
Collaborator

malandr2 commented Oct 4, 2023

Hi @bruce-yan,

I don't have a concrete timeline to share but it is something the greater team is aware of. I will follow-up here as soon as I have an update.

@andreasAasheim
Copy link

andreasAasheim commented Jan 19, 2024

Any news on this? @malandr2

@malandr2
Copy link
Collaborator

Hi @andreasAasheim, thanks for following up.

The team has looked this over and the error that is occurring here is done by design. The example app provided by OP tries to reuse ads from a list but the ads are already assigned into a View tree which is why there is the the error even if the tree has been removed from the stack.

To address @Patelparth2442, I don't think there are plans to enable multiple ad loads on one AdWidget.

Without getting too specific as it depends on app implementation, we recommend a different way to manage the preloaded banner ads. Thanks all.

@malandr2 malandr2 removed bug Something isn't working in triage Issue currently being evaluated p2-medium labels Jan 22, 2024
@large
Copy link
Author

large commented Jan 22, 2024

@malandr2 then how should this be handled correctly then? I am missing a good example for reuse of preloaded banner ads.
As for now I cannot thrust preloading at all and I loose precious seconds for loading each time my app is opened or page changes.

No preloading ensure that ads loads, but you get alot(!) of requests resulting in few impressions.
Reference to Admob stats in the front page
Screenshot_20240122-182400.png

It could take off some serverload to instruct us on how you would like this handled 😁

@malandr2
Copy link
Collaborator

Hey @large,

We recommend to clean the ad from the list and immediately load another one with the same size as the cleaned one and add it to the list.

See our response from our developers forum that may be applicable to this use case. Thanks!

@Patelparth2442
Copy link

@malandr2 then how should this be handled correctly then? I am missing a good example for reuse of preloaded banner ads. As for now I cannot thrust preloading at all and I loose precious seconds for loading each time my app is opened or page changes.

No preloading ensure that ads loads, but you get alot(!) of requests resulting in few impressions. Reference to Admob stats in the front page Screenshot_20240122-182400.png

It could take off some serverload to instruct us on how you would like this handled 😁

I can help you with this you match rate is too low probably you have to go with instant load and show ads method.

@large
Copy link
Author

large commented Jan 25, 2024

I can help you with this you match rate is too low probably you have to go with instant load and show ads method.

I figured out why the stats was so off, because of mediation. Screenshot is from front page of admob.

The "Reports" tab showed me that 1 request from the app generate x-number of requests to each mediation network (requests). The one that actally shows an ad (match request) is normally 1 (or 0 if no ad is return). Ref https://support.google.com/admob/answer/3436433

So it is not as bad as it looked in the first place
Screenshot_20240125-082610.png

@bmelnychenko
Copy link

@large does it mean, that I need to load bannerAd for each screen in my app.

I have the app with 5 screens.
home

  • screen1
  • screen2
  • screen3
  • screen4

I need Ad at every screen(including home). Do I need to make BannerAd and load it for every navigation to the screen?

So, instead of one load request per app, i will have a lot of load based on number of navigations?

Am I right? If yes - isn't it a violation of admob's policy?

Will appreciate any help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants