Skip to content

Commit

Permalink
[google_adsense] (Experimental) AdSense plugin for Flutter Web (#6871)
Browse files Browse the repository at this point in the history
Google Adsense plugin should allow Flutter Web developers to integrate Adsense more easily and monetize web project.

*List which issues are fixed by this PR. You must list at least one issue.*
flutter/flutter#40376
  • Loading branch information
sokoloff06 authored Dec 4, 2024
1 parent 71c9e77 commit ea90218
Show file tree
Hide file tree
Showing 35 changed files with 1,470 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ packages/flutter_migrate/** @stuartmorgan
packages/flutter_template_images/** @stuartmorgan
packages/go_router/** @chunhtai
packages/go_router_builder/** @chunhtai
packages/google_adsense/** @sokoloff06
packages/google_identity_services_web/** @ditman
packages/google_maps_flutter/** @stuartmorgan
packages/google_sign_in/** @stuartmorgan
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ These are the packages hosted in this repository:
| [flutter\_template\_images](./packages/flutter_template_images/) | [![pub package](https://img.shields.io/pub/v/flutter_template_images.svg)](https://pub.dev/packages/flutter_template_images) | [![pub points](https://img.shields.io/pub/points/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_template_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_template_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_template_images?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_template_images) |
| [go\_router](./packages/go_router/) | [![pub package](https://img.shields.io/pub/v/go_router.svg)](https://pub.dev/packages/go_router) | [![pub points](https://img.shields.io/pub/points/go_router)](https://pub.dev/packages/go_router/score) | [![popularity](https://img.shields.io/pub/popularity/go_router)](https://pub.dev/packages/go_router/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router) |
| [go\_router\_builder](./packages/go_router_builder/) | [![pub package](https://img.shields.io/pub/v/go_router_builder.svg)](https://pub.dev/packages/go_router_builder) | [![pub points](https://img.shields.io/pub/points/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![popularity](https://img.shields.io/pub/popularity/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router_builder?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router_builder) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router_builder?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router_builder) |
| [google\_adsense](./packages/google_adsense/)| [![pub package](https://img.shields.io/pub/v/google_adsense.svg)](https://pub.dev/packages/google_adsense) | [![pub points](https://img.shields.io/pub/points/google_adsense)](https://pub.dev/packages/google_adsense/score) | [![popularity](https://img.shields.io/pub/popularity/google_adsense)](https://pub.dev/packages/google_adsense/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_adsense?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_adsense) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_adsense?label=)](https://github.com/flutter/packages/labels/p%3A%20google_adsense) |
| [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) |
| [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) |
| [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) |
Expand Down
7 changes: 7 additions & 0 deletions packages/google_adsense/AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>

Google Inc.
The Chromium Authors
3 changes: 3 additions & 0 deletions packages/google_adsense/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.0.1

* Initial release.
25 changes: 25 additions & 0 deletions packages/google_adsense/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright 2013 The Flutter Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
120 changes: 120 additions & 0 deletions packages/google_adsense/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# google_adsense
[Google AdSense](https://adsense.google.com/intl/en_us/start/) plugin for Flutter Web

This package initializes AdSense on your website and provides an ad unit `Widget` that can be configured and placed in the desired location in your Flutter web app UI, without having to directly modify the HTML markup of the app directly.

## Disclaimer: Early Access ⚠️
This package is currently in early access and is provided as-is. While it's open source and publicly available, it's likely that you'll need to make additional customizations and configurations to fully integrate it with your Flutter Web App.
Please express interest joining Early Access program using [this form](https://docs.google.com/forms/d/e/1FAIpQLSdN6aOwVkaxGdxbVQFVZ_N4_UCBkuWYa-cS4_rbU_f1jK10Tw/viewform)

## Usage

### Setup your AdSense account
1. [Make sure your site's pages are ready for AdSense](https://support.google.com/adsense/answer/7299563?hl=en&sjid=5790642343077592212-EU&visit_id=638657100661171978-1373860041&ref_topic=1319756&rd=1)
2. [Create your AdSense account](https://support.google.com/adsense/answer/10162?hl=en&sjid=5790642343077592212-EU&visit_id=638657100661171978-1373860041&ref_topic=1250103&rd=1)

### Initialize AdSense
To start displaying ads, initialize the AdSense with your [client/publisher ID](https://support.google.com/adsense/answer/105516?hl=en&sjid=5790642343077592212-EU) (only use numbers).
<?code-excerpt "example/lib/main.dart (init)"?>
```dart
import 'package:google_adsense/experimental/google_adsense.dart';
void main() {
adSense.initialize(
'0123456789012345'); // TODO: Replace with your Publisher ID (pub-0123456789012345) - https://support.google.com/adsense/answer/105516?hl=en&sjid=5790642343077592212-EU
runApp(const MyApp());
}
```

### Enable Auto Ads
In order to start displaying [Auto ads](https://support.google.com/adsense/answer/9261805?hl=en) make sure to configure this feature in your AdSense Console. If you want to display ad units within your app content, continue to the next step

### Display ad unit Widget

1. Create [ad units](https://support.google.com/adsense/answer/9183549?hl=en&ref_topic=9183242&sjid=5790642343077592212-EU) in your AdSense account
2. Use relevant `AdUnitConfiguration` constructor as per table below

| Ad Unit Type | `AdUnitConfiguration` constructor method |
|----------------|--------------------------------------------|
| Display Ads | `AdUnitConfiguration.displayAdUnit(...)` |
| In-feed Ads | `AdUnitConfiguration.inFeedAdUnit(...)` |
| In-article Ads | `AdUnitConfiguration.inArticleAdUnit(...)` |
| Multiplex Ads | `AdUnitConfiguration.multiplexAdUnit(...)` |

3. Translate data-attributes from snippet generated in AdSense Console into constructor arguments as described below:
- drop `data-` prefix
- translate kebab-case to camelCase
- no need to translate `data-ad-client` as it the value was already passed at initialization

For example snippet below
```html
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-0123456789012345"
data-ad-slot="1234567890"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
```
translates into
<?code-excerpt "example/lib/main.dart (init-min)"?>
```dart
adSense.initialize(
'0123456789012345'); // TODO: Replace with your Publisher ID (pub-0123456789012345) - https://support.google.com/adsense/answer/105516?hl=en&sjid=5790642343077592212-EU
```
and
<?code-excerpt "example/lib/main.dart (adUnit)"?>
```dart
adSense.adUnit(AdUnitConfiguration.displayAdUnit(
adSlot: '1234567890', // TODO: Replace with your Ad Unit ID
adFormat: AdFormat
.AUTO, // Remove AdFormat to make ads limited by height
))
```

#### Customize ad unit Widget
To [modify your responsive ad code](https://support.google.com/adsense/answer/9183363?hl=en&ref_topic=9183242&sjid=11551379421978541034-EU):
1. Make sure to follow [AdSense policies](https://support.google.com/adsense/answer/1346295?hl=en&sjid=18331098933308334645-EU&visit_id=638689380593964621-4184295127&ref_topic=1271508&rd=1)
2. Use Flutter instruments for [adaptive and responsive design](https://docs.flutter.dev/ui/adaptive-responsive)

For example, when not using responsive `AdFormat` it is recommended to wrap adUnit widget in the `Container` with width and/or height constraints.
Note some [policies and restrictions](https://support.google.com/adsense/answer/9185043?hl=en#:~:text=Policies%20and%20restrictions) related to ad unit sizing:

<?code-excerpt "example/lib/main.dart (constraints)"?>
```dart
Container(
constraints:
const BoxConstraints(maxHeight: 100, maxWidth: 1200),
padding: const EdgeInsets.only(bottom: 10),
child: adSense.adUnit(AdUnitConfiguration.displayAdUnit(
adSlot: '1234567890', // TODO: Replace with your Ad Unit ID
adFormat: AdFormat
.AUTO, // Not using AdFormat to make ad unit respect height constraint
)),
),
```
## Testing and common errors

### Failed to load resource: the server responded with a status of 400
Make sure to set correct values to adSlot and adClient arguments

### Failed to load resource: the server responded with a status of 403
1. When happening in **testing/staging** environment it is likely related to the fact that ads are only filled when requested from an authorized domain. If you are testing locally and running your web app on `localhost`, you need to:
1. Set custom domain name on localhost by creating a local DNS record that would point `127.0.0.1` and/or `localhost` to `your-domain.com`. On mac/linux machines this can be achieved by adding the following records to you /etc/hosts file:
`127.0.0.1 your-domain.com`
`localhost your-domain.com`
2. Specify additional run arguments in IDE by editing `Run/Debug Configuration` or by passing them directly to `flutter run` command:
`--web-port=8080`
`--web-hostname=your-domain.com`
2. When happening in **production** it might be that your domain was not yet approved or was disapproved. Login to your AdSense account to check your domain approval status

### Ad unfilled

There is no deterministic way to make sure your ads are 100% filled even when testing. Some of the way to increase the fill rate:
- Try setting `adTest` parameter to `true`
- Try setting AD_FORMAT to `auto` (default setting)
- Try setting FULL_WIDTH_RESPONSIVE to `true` (default setting)
- Try resizing the window or making sure that ad unit Widget width is less than ~1200px
6 changes: 6 additions & 0 deletions packages/google_adsense/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## See https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#arguments
#override_platforms:
# chrome:
# settings:
# executable: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
# arguments: --no-sandbox
21 changes: 21 additions & 0 deletions packages/google_adsense/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# google_adsense_example

An example demonstrating google_adsense Flutter plugin usage

## Screenshots

![Screenshot of the test app showing an ad on mobile](../example/images/mobile_screenshot.png)
![Screenshot of the test app showing an ad on desktop](../example/images/desktop_screenshot.jpg)



## Testing

This package uses `package:integration_test` to run its tests in a web browser.

See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/blob/master/docs/ecosystem/testing/Plugin-Tests.md#web-tests)
in the Flutter documentation for instructions to set up and run the tests in this package.

Check [flutter.dev > Integration testing](https://docs.flutter.dev/testing/integration-tests)
for more info.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ea90218

Please sign in to comment.