首先,透過 Android Studio 創建一個專案,預設跑 Build -> Flutter -> Build APK 可以正常跑東西:
flutter build apk
💪 Building with sound null safety 💪
Running Gradle task 'assembleRelease'... 87.4s
✓ Built build/app/outputs/flutter-apk/app-release.apk (16.8MB).
Process finished with exit code 0
但是在 Project 視窗內,對 android 目錄 -> 右鍵 -> Flutter -> Open Android module in Android Studio 則是會看到:
Unsupported Java.
Your build is currently configured to use Java 19.0.2 and Gradle 7.5.
Possible solution:
- Open Gradle wrapper settings, change `distributionUrl` property to use compatible Gradle version and reload the project
解法就是 File -> Project structure -> Project
Android Gradle Plugin Version: 7.4.2
Gradle Version: 7.6.1
就可以很正常搞定了。
- 先到原本的 flutter 專案上,選擇 terminal -> flutter pub add google_mobile_ads ,運行一陣子後,回去看 pubspec.yaml 可以看到添加了 google_mobile_ads: ^2.3.0 項目
- 對 flutter 專案的 Project 視窗內的 android 目錄 -> 右鍵 -> Flutter -> Open Android module in Android Studio ,進入開發單純的 Android 專案一樣,要進行 AndroidManifest.xml 設定,主要是添加 Admob 使用的資訊
<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-3940256099942544~3347511713"/>
<application>
<manifest>
接著用 Build -> Rebuild Project 會看到數個錯誤訊息:
- Cannot fit requested classes in a single dex file (# methods: 68231 > 65536)
- Error: uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library [:google_mobile_ads]
解法就是在 build.gradle (Module: app) 更新:
defaultConfig {
// ...
minSdk 19
multiDexEnabled true
// ...
}
// ...
dependencies {
// ...
implementation "androidx.multidex:multidex:2.0.1"
// ...
}
3. 回到 flutter 專案上,編輯 lib/main.dart
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(const MyApp());
}
// ...
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final BannerAd myBanner = BannerAd(
// https://developers.google.com/admob/android/test-ads?hl=zh-cn#sample_ad_units
// https://developers.google.com/admob/ios/test-ads?hl=zh-cn#demo_ad_units
adUnitId: Platform.isAndroid ? 'ca-app-pub-3940256099942544/6300978111' : 'ca-app-pub-3940256099942544/2934735716' ,
size: AdSize.banner,
request: const AdRequest(),
listener: BannerAdListener(
// Called when an ad is successfully received.
onAdLoaded: (Ad ad) => print('Ad loaded.'),
// Called when an ad request failed.
onAdFailedToLoad: (Ad ad, LoadAdError error) {
// Dispose the ad here to free resources.
ad.dispose();
print('Ad failed to load: $error');
},
// Called when an ad opens an overlay that covers the screen.
onAdOpened: (Ad ad) => print('Ad opened.'),
// Called when an ad removes an overlay that covers the screen.
onAdClosed: (Ad ad) => print('Ad closed.'),
// Called when an impression occurs on the ad.
onAdImpression: (Ad ad) => print('Ad impression.'),
),
);
@override
void initState() {
super.initState();
myBanner.load();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.center,
width: myBanner.size.width.toDouble(),
height: myBanner.size.height.toDouble(),
child: AdWidget(ad: myBanner),
),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
以上就順玩流程,但仍有很多細節未處理,像是廣告沒 load 到,或是 App 或 View/ViewController 切換時廣告資源釋放等等。