kmcgill88 / admob_flutter

Admob Flutter plugin that shows banner ads using native platform views.
https://pub.dartlang.org/packages/admob_flutter
ISC License
434 stars 151 forks source link

Adding ad in iOS device calling build method continuously. #42

Open kmvignesh opened 5 years ago

kmvignesh commented 5 years ago

Hi, Today I tried to use this library to have ad on my app. I was working fine on android. But iOS devices was getting hanged all the time. So I did some debugging that time I found that adding "AdmobBanner" causing build method to call continuously.

import 'package:admob_flutter/admob_flutter.dart';
import 'package:flutter/material.dart';
import 'dart:io' show Platform;

const AD_MOB_APP_ID_ANDROID = "ca-app-pub-##########~2218177140";
const AD_MOB_BANNER_AD_ID_ANDROID = "ca-app-pub-##########/4022213402";
const AD_MOB_APP_ID_IOS = "ca-app-pub-##########~9065289542";
const AD_MOB_BANNER_AD_ID_IOS = "ca-app-pub-##########/4674440787";

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Admob.initialize(
        Platform.isAndroid ? AD_MOB_APP_ID_ANDROID : AD_MOB_APP_ID_IOS);
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        accentColor: Colors.blue,
        brightness: Brightness.light,
        appBarTheme: AppBarTheme(color: Colors.blue),
      ),
      home: HomePage(),
    );
  }
}

class AdPage extends StatefulWidget {
  @override
  _AdPageState createState() => _AdPageState();
}

class _AdPageState extends State<AdPage> {
  @override
  Widget build(BuildContext context) {
    print("AdPage build ");
    return Center(
      child: AdmobBanner(
        adUnitId: Platform.isAndroid
            ? AD_MOB_BANNER_AD_ID_ANDROID
            : AD_MOB_BANNER_AD_ID_IOS,
        adSize: AdmobBannerSize.BANNER,
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  initState() {
    super.initState();
    Admob.initialize(
        Platform.isAndroid ? AD_MOB_APP_ID_ANDROID : AD_MOB_APP_ID_IOS);
  }

  @override
  Widget build(BuildContext context) {
    print("HomePage build ");
    return Scaffold(
      body: Center(
        child: Container(
            child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Container(
              width: 200.0,
              height: 60.0,
              child: RaisedButton(
                child: Text(
                  "Show Ad",
                  style: TextStyle(fontSize: 21.0, color: Colors.white),
                ),
                color: Theme.of(context).accentColor,
                onPressed: () {
                  Navigator.push(
                      context, MaterialPageRoute(builder: (_) => AdPage()));
                },
                shape: new RoundedRectangleBorder(
                    borderRadius: new BorderRadius.circular(30.0)),
              ),
            ),
          ],
        )),
      ),
    );
  }
}

This is the sample project code I created to debug the issue.

Steps:

  1. Run the app on iOS device
  2. Tap on "Show Ad"
  3. Now the device will get hang. you can see the log, where it will print "AdPage build" too many times.
kmcgill88 commented 5 years ago

Thanks for the detailed bug report @kmvignesh! I have an app using this on iOS and it too will occasionally "hang", but it is inconsistent. It might be a second or two but then continues to render. Are you experiencing that too or does the app just lock up forever?

kmvignesh commented 5 years ago

Sorry for the delay. It is happening always for me. I tried to include that on my app. It was hanging after 2-4 sec. I used the same code on new project. In that project also its hanging.

diruuu commented 5 years ago

I experience this too. In my case it's because I'm using scope_models. If I remove the scope_models listeners, the continuously rebuilding issue won't happen. I don't why. This is the only reason why I stop using this package. But flutter doesn't have any alternative to show admob as widget, so I guess I need to wait for solution here.

kyleorin commented 5 years ago

I experience this too. In my case it's because I'm using scope_models. If I remove the scope_models listeners, the continuously rebuilding issue won't happen. I don't why. This is the only reason why I stop using this package. But flutter doesn't have any alternative to show admob as widget, so I guess I need to wait for solution here.

i can back this up with the provider package, same story.

diruuu commented 5 years ago

I think I found a temporary solution for this. I'm wrapping my widget with fragment package. What it does basically to prevent rerendering just like shouldComponentUpdate does in ReactJS. You should check it out https://pub.dev/packages/fragment

kyleorin commented 5 years ago

I think I found a temporary solution for this. I'm wrapping my widget with fragment package. What it does basically to prevent rerendering just like shouldComponentUpdate does in ReactJS. You should check it out https://pub.dev/packages/fragment

@diruuu this actually might work, would you mind providing a gist? possibly related to https://github.com/YoussefKababe/admob_flutter/issues/18#issuecomment-506290310

diruuu commented 5 years ago

I think I found a temporary solution for this. I'm wrapping my widget with fragment package. What it does basically to prevent rerendering just like shouldComponentUpdate does in ReactJS. You should check it out https://pub.dev/packages/fragment

@diruuu this actually might work, would you mind providing a gist? possibly related to #18 (comment)

I just simply wrap my widget with fragment widget.

return fragment((prev, prevKeys) { // previous result & previous keys. Both null on the first run
   return Text(text); // widgets subtree to cache
}, keys: [text]);

It does prevent the reloading, but you have to put in on the parent widget, and because of that now I need to compare all keys, which I think will be error prone since I'm using scope_model and I don't want my app stop being reactive. If only there's a way to stop admob_flutter only from being reloaded.

Do you find other solution for this? @CosmoInSpace

kyleorin commented 5 years ago

I think I found a temporary solution for this. I'm wrapping my widget with fragment package. What it does basically to prevent rerendering just like shouldComponentUpdate does in ReactJS. You should check it out https://pub.dev/packages/fragment

@diruuu this actually might work, would you mind providing a gist? possibly related to #18 (comment)

I just simply wrap my widget with fragment widget.

return fragment((prev, prevKeys) { // previous result & previous keys. Both null on the first run
   return Text(text); // widgets subtree to cache
}, keys: [text]);

It does prevent the reloading, but you have to put in on the parent widget, and because of that now I need to compare all keys, which I think will be error prone since I'm using scope_model and I don't want my app stop being reactive. If only there's a way to stop admob_flutter only from being reloaded.

Do you find other solution for this? @CosmoInSpace

No, this using fragments in my case, doesn't work. Not sure if I'm doing something wrong (not using keys for instance) although after some digging I think there needs to be an added pull request with AutomaticKeepAliveClientMixin as mentioned in https://github.com/YoussefKababe/admob_flutter/issues/18#issuecomment-506290310 and https://github.com/dreamsoftin/facebook_audience_network/commit/d29d594743cdc2c454c9db81c76e63ab8f8d124c.

Aside from that for some reason, if I decide to run on debugging iOS generates:

<Google> Cannot find an ad network adapter with the name(s): com.google.DummyAdapter. Remember to link all required ad network adapters and SDKs, and set -ObjC in the 'Other Linker Flags' setting of your build target.

And Android generates:

W/System  (23518): A resource failed to call release.
D/DynamitePackage(23518): Instantiating com.google.android.gms.ads.ChimeraAdManagerCreatorImpl
I/Ads     (23518): This request is sent from a test device.
E/BufferQueueProducer(23518): [SurfaceTexture-0-23518-3] cancelBuffer: BufferQueue has been abandoned
E/BufferQueueProducer(23518): [SurfaceTexture-0-23518-2] setAsyncMode: BufferQueue has been abandoned
E/BufferQueueProducer(23518): [SurfaceTexture-0-23518-2] cancelBuffer: BufferQueue has been abandoned
I/Choreographer(23518): Skipped 31 frames!  The application may be doing too much work on its main thread.
W/Ads     (23518): Not retrying to fetch app settings
E/Surface (23518): queueBuffer: error queuing buffer to SurfaceTexture, -19
E/EGL_emulation(23518): tid 23564: swapBuffers(552): error 0x300d (EGL_BAD_SURFACE)
W/OpenGLRenderer(23518): swapBuffers encountered EGL error 12301 on 0xddd86380, halting rendering...
D/EGL_emulation(23518): eglMakeCurrent: 0xddd85c00: ver 3 0 (tinfo 0xddd839a0)

Any opinions? @diruuu @kmcgill88

elowareKf commented 4 years ago

I solved this for me using a StatefulWidget as a container:

class TestContainer extends StatefulWidget{
  @override
  State<StatefulWidget> createState() => TestContainerState();

}

class TestContainerState extends State<TestContainer>{
  Widget banner;

  @override
  void initState() {

    super.initState();

    banner = AdHelper.getBanner();
  }

  @override
  Widget build(BuildContext context) {
    return banner;
  }

}

and then using the TestContainer.