TetsuFe / flutter_novel

Flutter製のノベルゲーム
https://tetsufe.github.io/flutter_novel/#/
3 stars 1 forks source link

広告を試す #17

Open TetsuFe opened 4 years ago

TetsuFe commented 4 years ago

ストーリーを読んだ後に広告を表示したい

Adsenseのライブラリはないのでplatform channelを使う https://stackoverflow.com/questions/57909791/is-it-possible-to-insert-google-adsense-at-flutter-web-application

参考

TetsuFe commented 4 years ago

warning対策 https://github.com/flutter/flutter/issues/41563#issuecomment-626750127

TetsuFe commented 4 years ago
  <script type="text/javascript">
    var nend_params = {"media":66283,"site":338979,"spot":1003266,"type":1,"oriented":1};
  </script>
  <script type="text/javascript" src="https://js1.nend.net/js/nendAdLoader.js"></script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>

このように記述したが、何も表示されなかった

TetsuFe commented 4 years ago

場所を指定して表示したい

https://pub.dev/packages/nend_plugin#-example-tab-

結論

場所は指定できない

nend公式(ファンコミュニケーションズ)のプラグインがあるっぽいので採用してみる=>ダメっぽい(後述。apiKeyが使えないとこのプラグインが使えないようだが、ios/androidしか対応していないようなので)

apiKeyというのが必要らしいが、表示されていない・・ 申請が承認される必要がある?

スクリーンショット 2020-06-03 22 00 31

apiKeyは、多分ios/androidでしか使えない https://github.com/fan-ADN/nendSDK-Flutter/blob/master/example/lib/banner.dart#L218

String get apiKey {
    print('apiKey selection : $_selection');
    switch (_selection) {
      case BannerSize.type728x90:
        return (Platform.isAndroid
            ? "02e6e186bf0183105fba7ce310dafe68ac83fb1c"
            : "2e0b9e0b3f40d952e6000f1a8c4d455fffc4ca3a");
      case BannerSize.type320x100:
        return (Platform.isAndroid
            ? "8932b68d22d1d32f5d7251f9897a6aa64117995e"
            : "eb5ca11fa8e46315c2df1b8e283149049e8d235e");
      case BannerSize.type300x100:
        return (Platform.isAndroid
            ? "1e36d1183d1ab66539998df4170a591c13028416"
            : "25eb32adddc4f7311c3ec7b28eac3b72bbca5656");
      case BannerSize.type300x250:
        return (Platform.isAndroid
            ? "499f011dbec5d37cfa388b749aed2bfff440a794"
            : "88d88a288fdea5c01d17ea8e494168e834860fd6");
      case BannerSize.type320x50:
      default:
        return (Platform.isAndroid
            ? "c5cb8bc474345961c6e7a9778c947957ed8e1e4f"
            : "a6eca9dd074372c898dd1df549301f277c53f2b9");
    }
  }
TetsuFe commented 4 years ago

広告が表示される条件

承認を待つ

https://affiliate150.com/nend-ad-setting

ここの「ステータス」という部分が「承認中」となっている間は審査中なので広告は表示されません。しばらくするとメールが来てこのステータスが「アクティブ」に変わります。「アクティブ」になっていれば広告は表示されているハズです。

なので、「nendの広告が表示されない」「広告が出ない」という場合にはこの点を確認して下さい。またWordPressの場合にはプラグインとの相性で広告が表示されない場合もありますので、一度プラグインを停止して確認しましょう。

承認後も数時間待つ

https://japanese-photographer.com/information_technology/nend_no-display/

ステータスがアクティブになっても、数時間は広告が表示されません。

スマホでしか表示されない

https://japanese-photographer.com/information_technology/nend_no-display/

Googleアドセンスと大きくことなる点ですが、うっかり忘れがちなのが「nendの広告はPCサイトには表示されない」という点です。

TetsuFe commented 4 years ago

特定のページだけで表示したい

特定のページだけで表示するには、ページにscriptタグをしこむしかない。逆に、index.htmlにしこむと全ページで広告が有効になってしまう。

ノベルゲームページでは広告が邪魔になってしまうことが考えられるので、特定のページだけに表示したいというわけ。

とりあえず、StoryListPageにこんな感じで挿入してみた

import 'dart:html';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter_state_management/story/story.dart';
import 'package:flutter_state_management/story/story_api.dart';
import 'package:flutter_state_management/story/story_details_page.dart';
import 'package:flutter_state_management/story/story_state_notifier.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

class StoryListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory('add_nend_script_1',
        (int viewId) {
      final element = ScriptElement()
        ..src = 'https://js1.nend.net/js/nendAdLoader.js'
        ..type = 'text/javascript';
      return element;
    });

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory('add_nend_script_2',
        (int viewId) {
      final element = ScriptElement()
        ..text = 'var nend_params = {"media":66283,'
            '"site":338979,"spot":1003266,"type":1,"oriented":1};'
        ..type = 'text/javascript';
      return element;
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('ストーリー選択'),
      ),
      body: SingleChildScrollView(
        child: Column(
           children: [
            const SizedBox(
              width: 0,
              height: 0,
              child: HtmlElementView(viewType: 'add_nend_script_1'),
            ),
            const SizedBox(
              width: 0,
              height: 0,
              child: HtmlElementView(viewType: 'add_nend_script_2'),
            ),
          ],
        ),
      ),
    );
  }
}
TetsuFe commented 4 years ago

エラー修正

https://aloerina01.github.io/javascript/2016/10/14/1.html

デバッグの際の注意点:非同期に読み込まれるので、待たないとエラーが表示されない

TetsuFe commented 4 years ago

いろんな方法でjavascriptを追加する

うまくいかない方法

innerHTML

index.htmlのbodyにscriptを仕込む方法

    ui.platformViewRegistry.registerViewFactory('add_nend_script_2',
        (int viewId) {
      final element = DivElement()
        ..appendHtml(
            '<script>var nend_params = {\'media\':66283,\'site\':338979,\'spot\':1003266,\'type\':1,\'oriented\':1};</script><script src=\'https://js1.nend.net/js/nendAdLoader.js\'></script>',
            validator: NodeValidatorBuilder.common()
              ..allowTextElements()
              ..allowElement('script', attributes: ['src']))
        ..id = 'plotly_div_id_';
      //..innerHtml =
      //   '<script>var nend_params = {\'media\':66283,\'site\':338979,\'spot\':1003266,\'type\':1,\'oriented\':1};</script><script src=\'https://js1.nend.net/js/nendAdLoader.js\'></script>';
      return element;

/* 省略 */

            const SizedBox(
              width: 0,
              height: 0,
              child: HtmlElementView(viewType: 'add_nend_script_2'),
            ),

普通にやると、Removing disallowed attribute・・となってしまうので、以下のようにして許可する https://stackoverflow.com/questions/18867266/dart-removing-disallowed-attribute-after-editor-upgraded

final NodeValidatorBuilder _htmlValidator=new NodeValidatorBuilder.common()
  ..allowElement('a', attributes: ['data-target', 'data-toggle'])
  ..allowElement('button', attributes: ['data-target', 'data-toggle']);

ScriptElementを使う

'に対してのエスケープが必要なことに注意すればよい。

https://stackoverflow.com/questions/57909791/is-it-possible-to-insert-google-adsense-at-flutter-web-application

import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'dart:html';

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    ui.platformViewRegistry.registerViewFactory("add_script", (int viewId) {
      ScriptElement element = ScriptElement()
        ..src = "https://js1.nend.net/js/nendAdLoader.js"
        ..type = "text/javascript";
      return element;
    });
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Directionality(
            textDirection: TextDirection.ltr,
            child: SizedBox(
              width: 640,
              height: 360,
              child: HtmlElementView(viewType: 'add_script'),
            ),
          ),
        ],
      ),
    );
  }
}

ただし、Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened. とエラーが出て動かない。

これは、以下を意味している

iframeを使う

https://aloerina01.github.io/javascript/2016/10/14/1.html

こちらの方法でもやってみたが、flutterがつくるcanvasに上書きされてしまうっぽい。実際に、buildメソッド上でエラーが出て止まった時などにはcanvasが作成されないため、広告が表示された。一番惜しい方法だったかもしれないが、これ以上進展がなさそうなものでもあった

TetsuFe commented 4 years ago

一応表示できた(実用的ではない)方法 iframeを使う

Scaffoldに包まずにMaterialApp直下に高さ0、幅0のWidgetをおくと、広告が表示された。

コード

import 'dart:html';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

class StoryListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory('add_nend_script_2',
        (int viewId) {
      final element = ScriptElement()
        ..async = true
        ..text = '(function() {'
            'var iframe = document.createElement("iframe");'
            'var body = document.getElementsByTagName("body")[0];'
            'body.appendChild(iframe);'
            'var html = "<body><script>var nend_params = {\'media\':66283,\'site\':338979,\'spot\':1003266,\'type\':1,\'oriented\':1};</script><script src=\'https://js1.nend.net/js/nendAdLoader.js\'></script></body>";'
            'var iframeDocument = iframe.contentWindow.document;'
            'iframeDocument.open();'
            'iframeDocument.write(html);'
            'iframeDocument.close();'
            '})();'
        ..type = 'text/javascript';
      return element;
    });

    return const SizedBox(
      width: 0,
      height: 0,
      child: HtmlElementView(viewType: 'add_nend_script_2'),
    );
  }
}

しかし、実際にはwidgetの裏のiframeが表示されている状態。 Widgetを追加すると、以下のように隠れる。

TetsuFe commented 4 years ago

iframeを使う方法(続き)

iframeとposition:absoluteの合わせ技を使うとflutterのwidgetの前面にiframeを表示できることがわかった。

    ui.platformViewRegistry.registerViewFactory('add_nend_script_2',
        (int viewId) {
      final element = ScriptElement()
        ..async = true
        ..text = '(function() {'
            'var iframe = document.createElement("iframe");'
            'iframe.style=\'position:absolute;\';'
            'var body = document.getElementsByTagName("body")[0];'
            'body.appendChild(iframe);'
            'var html = "<body><flt><script>var nend_params = {\'media\':66283,\'site\':338979,\'spot\':1003266,\'type\':1,\'oriented\':1};</script><script src=\'https://js1.nend.net/js/nendAdLoader.js\'></script></flt></body>";'
            'var iframeDocument = iframe.contentWindow.document;'
            'iframeDocument.open();'
            'iframeDocument.write(html);'
            'iframeDocument.close();'
            '})();'
        ..type = 'text/javascript';
      return element;
    });

    return SingleChildScrollView(
      child: Column(
        children: [
          // StoryList(),
          const SizedBox(
            width: 0,
            height: 0,
            child: HtmlElementView(viewType: 'add_nend_script_2'),
          ),
          IgnorePointer(
              child: Container(
            width: 300,
            height: 300,
            color: Colors.lightGreen,
          )),
          Container(
            width: 300,
            height: 300,
            color: Colors.red,
          ),
        ],
      ),
    );
  }
}

ちなみに、scaffoldでラップしてもちゃんと前面に出てくれる。

    return Scaffold(
      appBar: AppBar(
        title: const Text('ストーリー選択'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            // StoryList(),
            const SizedBox(
              width: 0,
              height: 0,
              child: HtmlElementView(viewType: 'add_nend_script_2'),
            ),
            IgnorePointer(
                child: Container(
              width: 300,
              height: 300,
              color: Colors.lightGreen,
            )),
            Container(
              width: 300,
              height: 300,
              color: Colors.red,
            ),
          ],
        ),
      ),
TetsuFe commented 4 years ago

現状実用性がなさそうなのでclose

TetsuFe commented 4 years ago

これも試したい https://twitter.com/_mono/status/1183555535368441856/photo/2