Source: Dev.to
You have an app idea. You want recurring revenue. You’re a solo developer with $25 to spare. Let’s build a SaaS mobile app that actually makes money.
The Architecture
Clean Architecture keeps your app maintainable as it grows. Here’s how the layers connect:
┌──────────────────────────────┐
│ FLUTTER APP │
└──────────────────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PRESENTATION │ │ DOMAIN │ │ DATA │
│───────────────│ │─────────────────│ │─────────────────│
│ • Widgets │ ──▶ │ • Use Cases │ ──▶ │ • Repositories │
│ • BLoC/Cubit │ │ • Entities │ │ • Data Sources │
│ • Riverpod │ │ • Business Logic│ │ • Models │
└───────────────┘ └─────────────────┘ └─────────────────┘
│
┌──────────────────────────────────┼──────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SUPABASE │ │ REVENUECAT │ │ LOCAL CACHE │
│─────────────────│ │─────────────────│ │─────────────────│
│ • PostgreSQL │ │ • Subscriptions │ │ • Hive/SQLite │
│ • Auth │ │ • Receipts │ │ • Shared Prefs │
│ • Storage │ │ • Analytics │ │ │
│ • Edge Functions│ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Step 1: The Costs Breakdown
Google Play Developer Account
| Item | Cost | Frequency |
|---|
| Registration | $25 | One‑time |
| Annual Fee | $0 | – |
| Apple (optional) | $99 | Per year |
Google Play Service Fees
| Revenue Tier | Google Takes | You Keep |
|---|
| First $1M/year | 15% | 85% |
| Above $1M/year | 30% | 70% |
| Subscriptions Year 1 | 15% | 85% |
| Subscriptions Year 2+ | 10% | 90% |
💡 Pro tip: Retain subscribers for 12+ months and Google only takes 10%.
Alternative Billing (EEA)
In the European Economic Area: 4% discount → 11% instead of 15%.
Step 2: Supabase Backend
Free Tier vs Pro Comparison
| Feature | Free Tier | Pro ($25/mo) |
|---|
| Database | 500 MB | 8 GB |
| Storage | 1 GB | 100 GB |
| Bandwidth | 5 GB | 250 GB |
| MAU | 50,000 | 100,000 |
| Edge Functions | 500K/mo | 2M/mo |
| Realtime | 200 conn | 500 conn |
| Backups | ❌ | Daily |
| Pausing | After 1 week | Never |
⚠️ Free projects pause after 1 week of inactivity. Upgrade when you have paying users.
Step 3: The Subscription Flow
USER JOURNEY
═══════════
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ User │ ──────▶ │ Paywall │ ──────▶ │ Google │ ──────▶ │ Success │
│ Taps │ │ Screen │ │ Play │ │ 🎉 │
│ Upgrade │ │ │ │ Billing │ │ │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ BACKEND FLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Google Play ──▶ RevenueCat ──▶ Webhook ──▶ Supabase Edge Func │
│ │ │ │ │ │
│ [Receipt] [Validate] [Notify] [Update DB] │
│ │
└─────────────────────────────────────────────────────────────────────┘
Step 4: Flutter Code Setup
Dependencies
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
supabase_flutter: ^2.0.0
purchases_flutter: ^6.0.0 # RevenueCat
flutter_bloc: ^8.1.0
get_it: ^7.6.0 # Dependency Injection
Initialize Supabase
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter/widgets.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
anonKey: 'YOUR_ANON_KEY',
);
runApp(MyApp());
}
final supabase = Supabase.instance.client;
RevenueCat Purchase Service
import 'package:purchases_flutter/purchases_flutter.dart';
class PurchaseService {
static Future init() async {
await Purchases.configure(
PurchasesConfiguration('your_revenuecat_api_key'),
);
}
static Future> getOfferings() async {
final offerings = await Purchases.getOfferings();
return offerings.current?.availablePackages ?? [];
}
static Future purchase(Package package) async {
try {
final result = await Purchases.purchasePackage(package);
return result.customerInfo.entitlements.active.containsKey('premium');
} catch (_) {
return false;
}
}
static Future isPremium() async {
final customerInfo = await Purchases.getCustomerInfo();
return customerInfo.entitlements.active.containsKey('premium');
}
}
Step 5: Pricing Strategy
Recommended Tiers
| Plan | Price | Conversion | Notes |
|---|
| Weekly | $2.99 | 47% choose this | Low commitment |
| Monthly | $9.99 | Standard | Most common |
| Yearly | $49.99 | Best value | 58% savings |
| Lifetime | $99.99 | High LTV | Price anchor |
Key Stats for 2025
| Metric | Value |
|---|
| Avg subscription price | $10.20/mo |
| 7‑day trial conversion | 5.2% |
| Trials boost LTV by | up to 64% |
| Weekly sub popularity | 47% of all subs |
📊 The Math: 100 users × $9.99 × 85% = $849/month
Real Indie Developer Income
Income Distribution
| Level | Monthly Revenue | Percentage |
|---|
| Most first apps | < $100 total | ~60% |
| Struggling | $0 – $500 | ~25% |
| Sustainable | $1K – $5K | ~10% |
| Successful | $5K – $20K | ~4% |
| Top performers | $20K+ | ~1% |
Success Stories
| App | Revenue | Timeline |
|---|
| Postiz | $2K MRR | 4 months |
| Formula Bot | $220K MRR | 18 months |
| ShipFast | $133K/month | Ongoing |
| Itemlist | $1.4K/month | 3 pricing tiers |
| Xnapper | Sold $150K | 21 months |
The Complete Tech Stack
Stack Overview
| Layer | Technology | Cost |
|---|
| Frontend | Flutter 3.x + Riverpod/BLoC | Free |
| Backend | Supabase (Postgres + Auth) | Free → $25/mo |
| Payments | RevenueCat | Free → % of revenue |
| Store | Google Play | $25 one‑time |
| Store (iOS) | App Store | $99/year |
Starting Cost
┌───