3
votes

I have a problem with Admob's widgets. I am developing a new feature for a flutter app that contains an Admob banner widget. But when I setState the value of another widget, the Admob Widget gets an error.

I am using : google_mobile_ads: ^0.11.0+1

The banner is build like so:

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

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

And the widget is display like this:

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

The log error catch : 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)

enter image description here

3
Can you please add error logs? seems like some info there but can't able to read on gif..jignesh
Could you pls link which of the ads packages you use? there's a bunch on pub.dev (pub.dev/packages?q=admob)galloper
I am also getting same error message.Shrijan Regmi

3 Answers

0
votes

As from question and error log, seems like you are trying to fresh screen/widget using setState but not the actual ad based container widget - still you are getting this error.

If I understood correctly.. then issue is about ad widget is trying to rebuild on setState call with older ad object but it expect new ad object everytime new build. So, avoid those kind of widget build if that's not required.

create a seprate ad based container widget like AppXyzAdWidget and move the ad's parent container code and other ad related code inside the new widget and use the newly created ad based widget on your screen.

This way you can decouple your screen without ad related things then after setState will not reload your ad and their widget just only refresh your content.

0
votes

Calling setState() causes the widget to rebuild, so your FutureBuilder() widget fires the future task again. This is why you're seeing this error.

You need to remove FutureBuilder() and move your future task to 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

You can also have boolean to make the AdWidget() appear when the future task is complete:

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
0
votes

Can you remove initState code and initialize _adBanner in your build function like this ?

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