Want to build a real-world Flutter app and earn with ads? In this step-by-step tutorial, you’ll learn how to create a To-Do List app in Flutter that uses Google AdMob for monetization and SharedPreferences for local task storage.
This guide is perfect for students, indie developers, or anyone looking to monetize a simple Flutter app using Cursor AI.
✅ What You’ll Learn
Building a feature-based Flutter app
Using SharedPreferences to save tasks locally
Integrating Google AdMob (banner and interstitial ads)
Managing state with Bloc
Working with Cursor AI to speed up development
🛠️ Prerequisites
Before you start, make sure you have the following ready:
Flutter SDK (3.0+)
Cursor AI or another AI-based coding tool
Android emulator or physical device
Google AdMob account
Basic understanding of Flutter and Dart
🪜 Step-by-Step Guide
Step 1: Create a New Flutter Project
flutter create todo_admob_app
cd todo_admob_app
Step 2: Open the Project in Cursor AI
Open Cursor AI.
Navigate to the
todo_admob_app
project folder.Paste your full app generation prompt (includes AdMob, SharedPreferences, Bloc, etc.).
Let Cursor AI generate your base code structure.
Step 3: Create a Google AdMob Account
Go to Google AdMob.
Sign in with your Google account.
Click Apps > Add App.
Select Android and enter your app name.
Copy the App ID (you’ll need it for your
AndroidManifest.xml
).
Step 4: Add Required Dependencies
Edit pubspec.yaml
and add:
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.0
shared_preferences: ^2.0.15
google_mobile_ads: ^3.0.0
Then run:
flutter pub get
Step 5: Initialize AdMob
In main.dart
:
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(MyApp());
}
In android/app/src/main/AndroidManifest.xml
, add:
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxx~yyyyyyyy" />
Replace with your actual AdMob App ID.
Step 6: Add AdMob Ads
Banner Ad
Add a banner ad at the bottom of the task list screen using the test Ad Unit ID:
BannerAd(
adUnitId: 'ca-app-pub-3940256099942544/6300978111', // test ID
size: AdSize.banner,
...
)
Interstitial Ad
Load and show an interstitial ad after adding or deleting a task:
InterstitialAd.load(
adUnitId: 'ca-app-pub-3940256099942544/1033173712', // test ID
...
);
⚠️ Always use test ad unit IDs during development!
📢 Understanding the Three Main AdMob Ad Types and How to Implement Them Simply
When you integrate Google AdMob in your Flutter To-Do List app, you have three primary ad formats to consider:
1. Banner Ads
What it is: A small rectangular ad displayed persistently, usually at the top or bottom of the screen.
Why use it: It offers steady ad impressions without disrupting user experience.
Where to place: At the bottom of your task list screen.
Implementation steps:
Create a
BannerAd
object with a test ad unit ID.Load the banner ad.
Display it inside a container aligned to the bottom.
2. Interstitial Ads
What it is: Full-screen ads that appear at natural transition points (e.g., after adding or deleting a task).
Why use it: Generates high revenue with less frequent but more engaging impressions.
When to show: After completing a user action like adding or deleting tasks.
Implementation steps:
Load an interstitial ad ahead of time.
Show it after the user finishes the action.
Handle ad dismissal and reload the next ad.
3. Rewarded Ads
What it is: Full-screen ads users watch voluntarily to earn rewards like unlocking app features.
Why use it: Encourages engagement and rewards users, increasing retention.
Use case: For example, users could watch a rewarded ad to unlock a new task priority color or theme.
Implementation steps:
Load a rewarded ad.
Show it when the user opts in.
Grant rewards once the ad is watched completely.
Reload for future use.
🛠️ Simple Implementation Guide for Each Ad Type
Initialize Mobile Ads SDK (Required for all ads)
In your main.dart
:
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(MyApp());
}
Banner Ad Implementation
BannerAd bannerAd = BannerAd(
adUnitId: 'ca-app-pub-3940256099942544/6300978111', // Test ID
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(
onAdLoaded: (ad) => print('Banner ad loaded.'),
onAdFailedToLoad: (ad, error) {
ad.dispose();
print('Failed to load banner ad: $error');
},
),
);
bannerAd.load();
// In the widget tree (e.g., Scaffold's bottom area)
Container(
width: bannerAd.size.width.toDouble(),
height: bannerAd.size.height.toDouble(),
child: AdWidget(ad: bannerAd),
)
Interstitial Ad Implementation
InterstitialAd? interstitialAd;
void loadInterstitialAd() {
InterstitialAd.load(
adUnitId: 'ca-app-pub-3940256099942544/1033173712', // Test ID
request: AdRequest(),
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (ad) {
interstitialAd = ad;
print('Interstitial ad loaded.');
},
onAdFailedToLoad: (error) {
print('Failed to load interstitial ad: $error');
},
),
);
}
void showInterstitialAd() {
if (interstitialAd == null) {
print('Interstitial ad not ready.');
return;
}
interstitialAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdDismissedFullScreenContent: (ad) {
ad.dispose();
loadInterstitialAd();
},
onAdFailedToShowFullScreenContent: (ad, error) {
ad.dispose();
print('Failed to show interstitial ad: $error');
loadInterstitialAd();
},
);
interstitialAd!.show();
interstitialAd = null;
}
Rewarded Ad Implementation
RewardedAd? rewardedAd;
void loadRewardedAd() {
RewardedAd.load(
adUnitId: 'ca-app-pub-3940256099942544/5224354917', // Test ID
request: AdRequest(),
rewardedAdLoadCallback: RewardedAdLoadCallback(
onAdLoaded: (ad) {
rewardedAd = ad;
print('Rewarded ad loaded.');
},
onAdFailedToLoad: (error) {
print('Failed to load rewarded ad: $error');
},
),
);
}
void showRewardedAd() {
if (rewardedAd == null) {
print('Rewarded ad not ready.');
return;
}
rewardedAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdDismissedFullScreenContent: (ad) {
ad.dispose();
loadRewardedAd();
},
onAdFailedToShowFullScreenContent: (ad, error) {
ad.dispose();
print('Failed to show rewarded ad: $error');
loadRewardedAd();
},
);
rewardedAd!.show(onUserEarnedReward: (ad, reward) {
print('User earned reward: ${reward.amount} ${reward.type}');
// TODO: Grant reward in your app here.
});
rewardedAd = null;
}
⚠️ Important Tips for All Ads
Use test ad unit IDs during development to avoid AdMob policy violations.
Load interstitial and rewarded ads before showing them to prevent delays.
Dispose ads properly after use to free up resources.
Display ads at natural points to avoid frustrating users.