flutterアプリにおいて広告の表示方法を備忘がてらにまとめておきます。
プログラミングの基礎の内容やさまざまなウィジェットの使い方などググっても見つけることが難しいような情報が詰まっています。
【重要】SDKバージョンを21以上に指定しましょう
以下のようなエラーが発生している場合、android/app/build.gradle のminSdkVersionを21以上にしておきましょう。
- No implementation found for method "MobileAds#initialize"
Google Mobile Ads SDK for Flutterのサポート範囲
Google Mobile Ads SDK for Flutterは以下のような内容をサポートしています。
- バナー広告の表示
- インタースティシャル広告の表示
- リワード付き動画広告の読み込みと表示
- ネイティブ広告の表示
Mobile Ads SDKをインポートする
pubspec.yamlにgoogle_mobile_adsを追加する
pubspec.yamlファイルに以下のように記載します。
dependencies:
flutter:
sdk: flutter
google_mobile_ads: ^2.0.0
flutter pub get でインストールする
flutter pub get
importする
広告表示をしたいファイルで以下の一行を追加します。
import 'package:google_mobile_ads/google_mobile_ads.dart';
プラットフォームごとの設定(iOS/Android)
iOS用に設定する
Info.plistを更新する
以下のように、AdMobアプリIDをGADApplicationIdentifierキーに追加する。
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-################~##########</string>
Android用に設定する
AndroidManifest.xmlを更新する
AdMobアプリIDがAndroidManifest.xmlに含まれていなければアプリ起動時にクラッシュします。
以下のようにAdMobアプリIDを更新します。
<manifest>
<application>
<!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>
</application>
</manifest>
Mobile Ads SDKの初期化
ここまでで準備は完了です。
次は実際に広告を表示させる前に初期化処理を実装しておきます。
MobileAds.instance.initialize()を呼び出す
広告を読み込む前に、MobileAds.instance.initialize()を呼び出してアプリにMobile Ads SDKを初期化させ流必要があります。理想的にはアプリを実行する直前に実行する必要があります。
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() {
//以下の二行を追加
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
//追加ここまで
runApp(MyApp());
}
広告を表示する
上述しましたが、以下のような広告を表示させることが出来ます。
- バナー広告の表示
- インタースティシャル広告の表示
- ネイティブ広告の表示
- リワード付き動画広告の読み込みと表示
バナー広告を表示する
バナー広告はアプリの上部か下部のどちらかに表示させることができます。ユーザーがアプリを操作している間は画面に表示され続け、一定時間後に自動的に更新されます。
バナー広告を初期化する
前置きが長くなってしまいましたが、バナー広告を表示させてみましょう。
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final BannerAd myBanner = BannerAd(
//TEST ANDROID : ca-app-pub-3940256099942544/6300978111
//TEST IOS : ca-app-pub-3940256099942544/2934735716
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/6300978111'
: 'ca-app-pub-3940256099942544/2934735716',
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(
onAdLoaded: (Ad ad) => print('バナー広告がロードされました'),
// Called when an ad request failed.
onAdFailedToLoad: (Ad ad, LoadAdError error) {
// Dispose the ad here to free resources.
ad.dispose();
print('バナー広告の読み込みが次の理由で失敗しました: $error');
},
// Called when an ad opens an overlay that covers the screen.
onAdOpened: (Ad ad) => print('バナー広告が開かれました'),
// Called when an ad removes an overlay that covers the screen.
onAdClosed: (Ad ad) => print('バナー広告が閉じられました'),
// Called when an impression occurs on the ad.
onAdImpression: (Ad ad) => print('Ad impression.'),
),
);
//.......
//.......
}
バナー広告を読み込む
BannerAdがインスタンス化された後、画面に表示する前にload()を呼び出す必要があります。
@override
Widget build(BuildContext context) {
myBanner.load();
//........
}
バナー広告を表示する
BannerAdをウィジェットとして表示するには、load()を呼び出した後、AdWidgetをインスタンス化します。
@override
Widget build(BuildContext context) {
myBanner.load();
final AdWidget adWidget = AdWidget(ad: myBanner);
//.......
}
AdWidgetはFlutterのWidgetクラスを継承しているため、他のウィジェットと同じように使用できます。 iOSでは、ウィジェットを指定された幅と高さのウィジェットに配置してください。 そうしないと、広告が表示されない場合があります。 BannerAdは、広告と一致するサイズのコンテナに配置できます。
@override
Widget build(BuildContext context) {
myBanner.load();
final AdWidget adWidget = AdWidget(ad: myBanner);
final Container adContainer = Container(
alignment: Alignment.center,
child: adWidget,
width: myBanner.size.width.toDouble(),
height: myBanner.size.height.toDouble(),
);
//......
}
以下のように作成したContainerを表示させてみましょう。
children: <Widget>[
adContainer,
],
表示された画面のキャプチャ
バナー広告表示時にエラーが出る?
次のようなエラーが出ることがあります。そんな時は、build.gradleを以下のように書き換えます。
uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library [:google_mobile_ads] /Users/uedive/AndroidStudioProjects/quiz_share/build/google_mobile_ads/intermediates/library_manifest/debug/AndroidManifest.xml as the library might be using APIs not available in 16
Suggestion: use a compatible library with a minSdk of at most 16,
or increase this project's minSdk version to at least 19,
or use tools:overrideLibrary="io.flutter.plugins.googlemobileads" to force usage (may lead to runtime failures)
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "XXX.XXXXX.XXX"
minSdkVersion 19
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
バナーサイズについて
上記のコードにてsize: AdSize.bannerとありますが、バナー広告のサイズを変更することも可能です。
サイズ(dp) | サイズの説明 | AdSize定数 |
---|---|---|
320x50 | 標準的なバナー | banner |
320x100 | 大きめのバナー | largeBanner |
320x250 | 中程度の長方形 | mediumRectangle |
468x60 | フルサイズバナー | fullBanner |
728x90 | リーダーボード | leaderboard |
任意のサイズのバナーを表示したい場合は以下のように指定することも可能です。
final AdSize adSize = AdSize(300, 50);
バナー広告のイベントについて
AdListenerを利用すると、広告が閉じられたときやユーザーがアプリを閉じたときなどのイベントをリッスンすることができます。
この際はバナー広告の初期化の際に以下のコードを利用してください。
final BannerAd myBanner = BannerAd(
adUnitId: getTestAdBannerUnitId(),
size: AdSize.banner,
request: AdRequest(),
listener: AdListener(
// 広告が正常にロードされたときに呼ばれます。
onAdLoaded: (Ad ad) => print('バナー広告がロードされました。'),
// 広告のロードが失敗した際に呼ばれます。
onAdFailedToLoad: (Ad ad, LoadAdError error) {
print('バナー広告のロードに失敗しました。: $error');
},
// 広告が開かれたときに呼ばれます。
onAdOpened: (Ad ad) => print('バナー広告が開かれました。'),
// 広告が閉じられたときに呼ばれます。
onAdClosed: (Ad ad) => print('バナー広告が閉じられました。'),
// ユーザーがアプリを閉じるときに呼ばれます。
onApplicationExit: (Ad ad) => print('ユーザーがアプリを離れました。'),
),
);
インタースティシャル(Interstitial)広告を表示する
インタースティシャル広告はフルスクリーンの広告です。ユーザーは広告をタップするか閉じるボタンを押すか選択できます。
インタースティシャル広告を初期化する
//プラットホームごとのテスト広告IDを取得するメソッドをどこかに配置してください。
String getTestAdInterstitialUnitId(){
String testBannerUnitId = "";
if(Platform.isAndroid) {
// Android のとき
testBannerUnitId = "ca-app-pub-3940256099942544/1033173712";
} else if(Platform.isIOS) {
// iOSのとき
testBannerUnitId = "ca-app-pub-3940256099942544/4411468910";
}
return testBannerUnitId;
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final InterstitialAd myInterstitial = InterstitialAd(
adUnitId: getTestAdInterstitialUnitId(),
request: AdRequest(),
listener: AdListener(),
);
//.......
//.......
}
インタースティシャル広告を読み込む
InterstitialAdがインスタンス化された後、画面に表示する前にload()を呼び出す必要があります。
@override
Widget build(BuildContext context) {
myInterstitial.load();
//........
}
インタースティシャル広告を表示する
インタースティシャル広告はアプリの全面に静的に配置されるため、Flutterウィジェットツリーに追加することはできません。
そのため、広告が読み込まれた後にshow()を呼び出すことでインタースティシャル広告を表示させます。
今回はデフォルトで作成されている_incrementCounter()でshow()を呼び出すことでカウンターがプラスされたときに広告を表示させるようにしてみました。
@override
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
// ここに追加しました!!
myInterstitial.show();
// ここに追加しました!!
_counter++;
});
}
//..........
}
インタースティシャル広告のイベントについて
AdListenerを利用すると、広告が閉じられたときやユーザーがアプリを閉じたときなどのイベントをリッスンすることができます。
「インタースティシャル広告を閉じられたときに画面遷移する」などの処理の実装はここで実施しましょう。
この場合、インタースティシャル広告の初期化の際に以下のコードを使いましょう。
final InterstitialAd myInterstitial = InterstitialAd(
adUnitId: getTestAdInterstitialUnitId(),
request: AdRequest(),
listener: AdListener(
// 広告が正常にロードされたときに呼ばれます。
onAdLoaded: (Ad ad) => print('インタースティシャル広告がロードされました。'),
// 広告のロードが失敗した際に呼ばれます。
onAdFailedToLoad: (Ad ad, LoadAdError error) {
print('インタースティシャル広告のロードに失敗しました。: $error');
},
// 広告が開かれたときに呼ばれます。
onAdOpened: (Ad ad) => print('インタースティシャル広告が開かれました。'),
// 広告が閉じられたときに呼ばれます。
onAdClosed: (Ad ad) => print('インタースティシャル広告が閉じられました。'),
// ユーザーがアプリを閉じるときに呼ばれます。
onApplicationExit: (Ad ad) => print('ユーザーがアプリを離れました。'),
),
);
報酬付き(リワード)広告(Rewarded Ads)を表示する
報酬付き広告(リワード広告)はユーザーがアプリ内で取得できる報酬と引き換えに表示する広告です。
リワード広告を初期化する
ロードしたRewardedAdで複数回show()を呼び出さないようにする必要があります。
そのため、AdListener.onAdFailedToLoad()とAdListener.onAdClosed()でdispose()を読んでおきましょう。
また、報酬を獲得した後もdispose()されるようにイベントに追加しておきましょう。
広告のイベントも込みの初期化コードを以下に示しておきます。
//プラットホームごとのテスト広告IDを取得するメソッドをどこかに配置してください。
String getTestAdRewardedUnitId(){
String testBannerUnitId = "";
if(Platform.isAndroid) {
// Android のとき
testBannerUnitId = "ca-app-pub-3940256099942544/5224354917";
} else if(Platform.isIOS) {
// iOSのとき
testBannerUnitId = "ca-app-pub-3940256099942544/1712485313";
}
return testBannerUnitId;
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final RewardedAd myRewarded = RewardedAd(
adUnitId: getTestAdRewardedUnitId(),
request: AdRequest(),
listener: AdListener(
// Called when an ad is successfully received.
onAdLoaded: (Ad ad) => print('リワード広告を読み込みました。'),
// Called when an ad request failed.
onAdFailedToLoad: (Ad ad, LoadAdError error) {
ad.dispose();
print('リワード広告の読み込みに失敗しました。: $error');
},
// Called when an ad opens an overlay that covers the screen.
onAdOpened: (Ad ad) => print('リワード広告が開かれました。'),
// Called when an ad removes an overlay that covers the screen.
onAdClosed: (Ad ad) => print('リワード広告が閉じられました。'),
// Called when an ad is in the process of leaving the application.
onApplicationExit: (Ad ad) => print('ユーザーがアプリを離れました。'),
// Called when a RewardedAd triggers a reward.
onRewardedAdUserEarnedReward: (RewardedAd ad, RewardItem reward) {
ad.dispose();
print('報酬を獲得しました!: $reward');
},
),
);
//.......
//.......
}
リワード広告を読み込む
RewardedAdがインスタンス化された後、画面に表示する前にload()を呼び出す必要があります。
@override
Widget build(BuildContext context) {
myRewarded.load();
//........
}
リワード広告を表示する
リワード広告はアプリの全面に静的に配置されるため、Flutterウィジェットツリーに追加することはできません。
そのため、広告が読み込まれた後にshow()を呼び出すことでリワード広告を表示させます。
今回はデフォルトで作成されている_incrementCounter()でshow()を呼び出すことでカウンターがプラスされたときに広告を表示させるようにしてみました。
@override
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
// ここに追加しました!!
myRewarded.show();
// ここに追加しました!!
_counter++;
});
}
//..........
}
リワード広告の実行結果
I/flutter (16030): 報酬を獲得しました!: Instance of 'RewardItem'
すぐに使える
import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(const MyApp());
}
const String testDevice = 'YOUR_DEVICE_ID';
const int maxFailedLoadAttempts = 3;
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyAdPageState();
}
class _MyAdPageState extends State<MyHomePage> {
static final AdRequest request = AdRequest(
);
InterstitialAd? _interstitialAd;
int _numInterstitialLoadAttempts = 0;
RewardedAd? _rewardedAd;
int _numRewardedLoadAttempts = 0;
RewardedInterstitialAd? _rewardedInterstitialAd;
int _numRewardedInterstitialLoadAttempts = 0;
//バナー
final BannerAd myBanner = BannerAd(
//TEST ANDROID : ca-app-pub-3940256099942544/6300978111
//TEST IOS : ca-app-pub-3940256099942544/2934735716
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/6300978111'
: 'ca-app-pub-3940256099942544/2934735716',
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(
onAdLoaded: (Ad ad) => print('バナー広告がロードされました'),
// Called when an ad request failed.
onAdFailedToLoad: (Ad ad, LoadAdError error) {
// Dispose the ad here to free resources.
ad.dispose();
print('バナー広告の読み込みが次の理由で失敗しました: $error');
},
// Called when an ad opens an overlay that covers the screen.
onAdOpened: (Ad ad) => print('バナー広告が開かれました'),
// Called when an ad removes an overlay that covers the screen.
onAdClosed: (Ad ad) => print('バナー広告が閉じられました'),
// Called when an impression occurs on the ad.
onAdImpression: (Ad ad) => print('Ad impression.'),
),
);
@override
void initState() {
super.initState();
_createInterstitialAd();
_createRewardedAd();
_createRewardedInterstitialAd();
//バナーのロードを始める
myBanner.load();
}
void _createInterstitialAd() {
InterstitialAd.load(
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/1033173712'
: 'ca-app-pub-3940256099942544/4411468910',
request: request,
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (InterstitialAd ad) {
print('$ad ロードが完了しました。');
_interstitialAd = ad;
_numInterstitialLoadAttempts = 0;
_interstitialAd!.setImmersiveMode(true);
},
onAdFailedToLoad: (LoadAdError error) {
print('InterstitialAd のロードが失敗しました: $error.');
_numInterstitialLoadAttempts += 1;
_interstitialAd = null;
if (_numInterstitialLoadAttempts < maxFailedLoadAttempts) {
_createInterstitialAd();
}
},
)
);
}
void _showInterstitialAd() {
if (_interstitialAd == null) {
print('注意: interstitial広告が読み込まれる前に表示しようとしています.');
return;
}
_interstitialAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdShowedFullScreenContent: (InterstitialAd ad) =>
print('ad onAdShowedFullScreenContent.'),
onAdDismissedFullScreenContent: (InterstitialAd ad) {
print('$ad onAdDismissedFullScreenContent(閉じたとき).');
ad.dispose();
_createInterstitialAd();
},
onAdFailedToShowFullScreenContent: (InterstitialAd ad, AdError error) {
print('$ad onAdFailedToShowFullScreenContent: $error');
ad.dispose();
_createInterstitialAd();
},
);
_interstitialAd!.show();
_interstitialAd = null;
}
void _createRewardedAd() {
RewardedAd.load(
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/5224354917'
: 'ca-app-pub-3940256099942544/1712485313',
request: request,
rewardedAdLoadCallback: RewardedAdLoadCallback(
onAdLoaded: (RewardedAd ad) {
print('$ad loaded.');
_rewardedAd = ad;
_numRewardedLoadAttempts = 0;
},
onAdFailedToLoad: (LoadAdError error) {
print('RewardedAd failed to load: $error');
_rewardedAd = null;
_numRewardedLoadAttempts += 1;
if (_numRewardedLoadAttempts < maxFailedLoadAttempts) {
_createRewardedAd();
}
},
));
}
void _showRewardedAd() {
if (_rewardedAd == null) {
print('Warning: attempt to show rewarded before loaded.');
return;
}
_rewardedAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdShowedFullScreenContent: (RewardedAd ad) =>
print('ad onAdShowedFullScreenContent.'),
onAdDismissedFullScreenContent: (RewardedAd ad) {
print('$ad onAdDismissedFullScreenContent.');
ad.dispose();
_createRewardedAd();
},
onAdFailedToShowFullScreenContent: (RewardedAd ad, AdError error) {
print('$ad onAdFailedToShowFullScreenContent: $error');
ad.dispose();
_createRewardedAd();
},
);
_rewardedAd!.setImmersiveMode(true);
_rewardedAd!.show(
onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
print('$ad with reward $RewardItem(${reward.amount}, ${reward.type})');
});
_rewardedAd = null;
}
void _createRewardedInterstitialAd() {
RewardedInterstitialAd.load(
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/5354046379'
: 'ca-app-pub-3940256099942544/6978759866',
request: request,
rewardedInterstitialAdLoadCallback: RewardedInterstitialAdLoadCallback(
onAdLoaded: (RewardedInterstitialAd ad) {
print('$ad loaded.');
_rewardedInterstitialAd = ad;
_numRewardedInterstitialLoadAttempts = 0;
},
onAdFailedToLoad: (LoadAdError error) {
print('RewardedInterstitialAd failed to load: $error');
_rewardedInterstitialAd = null;
_numRewardedInterstitialLoadAttempts += 1;
if (_numRewardedInterstitialLoadAttempts < maxFailedLoadAttempts) {
_createRewardedInterstitialAd();
}
},
));
}
void _showRewardedInterstitialAd() {
if (_rewardedInterstitialAd == null) {
print('Warning: attempt to show rewarded interstitial before loaded.');
return;
}
_rewardedInterstitialAd!.fullScreenContentCallback =
FullScreenContentCallback(
onAdShowedFullScreenContent: (RewardedInterstitialAd ad) =>
print('$ad onAdShowedFullScreenContent.'),
onAdDismissedFullScreenContent: (RewardedInterstitialAd ad) {
print('$ad onAdDismissedFullScreenContent.');
ad.dispose();
_createRewardedInterstitialAd();
},
onAdFailedToShowFullScreenContent:
(RewardedInterstitialAd ad, AdError error) {
print('$ad onAdFailedToShowFullScreenContent: $error');
ad.dispose();
_createRewardedInterstitialAd();
},
);
_rewardedInterstitialAd!.setImmersiveMode(true);
_rewardedInterstitialAd!.show(
onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
print('$ad with reward $RewardItem(${reward.amount}, ${reward.type})');
});
_rewardedInterstitialAd = null;
}
@override
void dispose() {
super.dispose();
_interstitialAd?.dispose();
_rewardedAd?.dispose();
_rewardedInterstitialAd?.dispose();
myBanner.dispose();
}
@override
Widget build(BuildContext context) {
final AdWidget adWidget = AdWidget(ad: myBanner);
final Container adContainer = Container(
alignment: Alignment.center,
child: adWidget,
width: myBanner.size.width.toDouble(),
height: myBanner.size.height.toDouble(),
);
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
title: const Text('AdMob Plugin example app'),
actions: <Widget>[
PopupMenuButton<String>(
onSelected: (String result) {
switch (result) {
case 'InterstitialAd':
_showInterstitialAd();
break;
case 'RewardedAd':
_showRewardedAd();
break;
case 'RewardedInterstitialAd':
_showRewardedInterstitialAd();
break;
default:
throw AssertionError('unexpected button: $result');
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'InterstitialAd',
child: Text('InterstitialAd'),
),
PopupMenuItem<String>(
value: 'RewardedAd',
child: Text('RewardedAd'),
),
PopupMenuItem<String>(
value: 'RewardedInterstitialAd',
child: Text('RewardedInterstitialAd'),
),
],
),
],
),
body: SafeArea(child: Column(
children: [
Expanded(
child: Container(),
),
SizedBox(
width: double.infinity,
child: adContainer,
),
]
)
),
);
}
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}