monetix_flutter 0.2.3
monetix_flutter: ^0.2.3 copied to clipboard
A production-ready monetization policy layer for Flutter. Manages ads, premium state, consent, and rewarded ad-free breaks.
import 'package:flutter/material.dart';
import 'package:monetix_flutter/monetix_flutter.dart';
import 'package:provider/provider.dart';
import 'providers/revenue_cat_ad_status_provider.dart';
import 'screens/home_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Provider.debugCheckInvalidValueType = null;
runApp(const MonetixPlaygroundApp());
}
/// Captures Provider-resolved instances and wires them into the static
/// [Monetix] facade so debug panels and coordinator can access them
/// without being descendants of this Provider tree.
class _MonetixWire extends StatefulWidget {
final Widget child;
const _MonetixWire({required this.child});
@override
State<_MonetixWire> createState() => _MonetixWireState();
}
class _MonetixWireState extends State<_MonetixWire> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _wire());
}
void _wire() {
if (!mounted) return;
try {
Monetix.wire(
service: context.read<MonetizationService>(),
rewarded: context.read<RewardedMonetizationService>(),
gate: context.read<MonetizationGate>(),
config: context.read<IAdConfigProvider>(),
status: context.read<IAdStatusProvider>(),
analytics: context.read<IAdAnalytics>(),
// Pass null so wire() reuses the coordinator already registered
// by a prior bootstrap() or wire() call instead of creating an orphan.
coordinator: null,
);
} catch (e) {
debugPrint('[Monetix] _wire() failed — Provider tree may have been disposed: $e');
}
}
@override
Widget build(BuildContext context) => widget.child;
}
class MonetixPlaygroundApp extends StatelessWidget {
const MonetixPlaygroundApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// 1. Premium State Provider (Mocking RevenueCat)
ChangeNotifierProvider<RevenueCatAdStatusProvider>(
create: (_) => RevenueCatAdStatusProvider(),
),
// Expose as interface for the framework widgets
ListenableProxyProvider<RevenueCatAdStatusProvider, IAdStatusProvider>(
update: (_, status, __) => status,
),
// 2. Ad Configuration Provider (with Debug overrides)
ChangeNotifierProvider<DebugAdConfig>(
create: (_) => DebugAdConfig(),
),
// Expose as interface
ListenableProxyProvider<DebugAdConfig, IAdConfigProvider>(
update: (_, config, __) => config,
),
// 3. Analytics Provider (wrapped in DiagnosticAdAnalytics for timeline)
Provider<IAdAnalytics>(
create: (_) => DiagnosticAdAnalytics(PlaygroundAnalytics()),
),
// 4. Rewarded Service
ChangeNotifierProxyProvider2<DebugAdConfig, IAdAnalytics, RewardedMonetizationService>(
create: (context) => RewardedMonetizationService(
context.read<DebugAdConfig>(),
statusProvider: context.read<RevenueCatAdStatusProvider>(),
analyticsService: context.read<IAdAnalytics>(),
),
update: (_, config, analytics, previous) => previous!,
),
// 5. Main Monetization Orchestrator
Provider<MonetizationService>(
create: (context) {
final service = MonetizationService(
context.read<DebugAdConfig>(),
statusProvider: context.read<RevenueCatAdStatusProvider>(),
analyticsService: context.read<IAdAnalytics>(),
rewardedAdService: context.read<RewardedMonetizationService>(),
);
service.init();
return service;
},
dispose: (_, service) => service.dispose(),
),
// 6. Centralized Monetization Gate for UI ad visibility
ChangeNotifierProvider<MonetizationGate>(
create: (context) {
final gate = MonetizationGate(
configProvider: context.read<IAdConfigProvider>(),
statusProvider: context.read<IAdStatusProvider>(),
rewardedService: context.read<RewardedMonetizationService>(),
);
context.read<MonetizationService>().gate = gate;
context.read<RewardedMonetizationService>().gate = gate;
return gate;
},
),
],
child: _MonetixWire(
child: MaterialApp(
title: 'Monetix Playground',
debugShowCheckedModeBanner: false,
theme: _buildPremiumTheme(),
home: const HomeScreen(),
),
),
);
}
ThemeData _buildPremiumTheme() {
final base = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
primary: Colors.deepPurple,
secondary: Colors.amber,
),
);
return base.copyWith(
appBarTheme: const AppBarTheme(
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
titleTextStyle: TextStyle(
color: Colors.black87,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: Colors.grey.shade200),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
),
);
}
}
/// A simple analytics logger for the playground
class PlaygroundAnalytics extends IAdAnalytics {
@override
Future<void> logAdRequest({required String adType, required String adUnitId, required String screen, required String placement}) async {
debugPrint('📊 [Analytics] Request: $adType | Screen: $screen | Placement: $placement');
}
@override
Future<void> logAdImpression({required String adType, required String adUnitId, required String screen, required String placement, int? loadDurationMs, bool isFallback = false}) async {
debugPrint('📊 [Analytics] Impression: $adType | Fallback: $isFallback | Load: ${loadDurationMs}ms');
}
@override
Future<void> logAdFailure({required String adType, required String adUnitId, required String errorCode, required String screen, required String placement}) async {
debugPrint('📊 [Analytics] FAILURE: $adType | Error: $errorCode');
}
@override
Future<void> logAdRevenue({required double value, required String currency, required String adType, required String adUnitId, required String screen, required String placement}) async {
debugPrint('📊 [Analytics] Revenue: $value $currency ($adType)');
}
@override
Future<void> logAdRewardEarned({required String adType, required String screen, required String placement}) async {
debugPrint('📊 [Analytics] REWARD EARNED! Type: $adType');
}
@override
void startPostAdWindow(String adType) {
debugPrint('📊 [Analytics] Starting Post-Ad Retention Window ($adType)');
}
}