bosc_revenuecat_package (1.0.1)

Published 2025-12-17 06:05:13 +00:00 by mansi.kansara

Installation

dart pub add bosc_revenuecat_package:1.0.1 --hosted-url=

About this package

A simplified Flutter package for RevenueCat subscriptions with modern SDK methods

RevenueCat Subscription

Pub
License: MIT
Flutter

A simplified, modern Flutter package for seamless RevenueCat integration. Focus on your UI—everything else (purchases, status checks, real-time updates) is handled. Covers 100% of common use cases with clean APIs and no custom models.

Compatible with RevenueCat SDK v9.10.0+ (as of Dec 2025). Built on official RevenueCat Flutter SDK.

Why This Package?

RevenueCat abstracts StoreKit (iOS) and Google Play Billing (Android) complexities like receipts, entitlements, and cross-platform sync. This wrapper adds:

  • Widget for Paywalls: Auto-manages state, loading, errors, and eligibility.
  • Global Client: App-wide checks without reinitializing.
  • Best Practices: Logging, fresh/cached fetches, iOS-only guards (e.g., trials).
  • No Boilerplate: Direct SDK types; just design your UI.

Ideal for freemium apps, ad-free upgrades, or premium features.

Features

Feature Description
Easy Setup One-time config with iOS/Android keys.
Purchases Buy packages/products; handles cancellations gracefully.
Status Checks Active entitlements, packages, granular statuses (active/inactive/expired/billingIssue).
Trials/Intros Eligibility detection for UI badges (iOS-only).
Real-Time Global stream for updates (e.g., background buys).
User Mgmt Login/logout, attributes, email/phone.
Tools Restore/sync, manage URL, code redemption (iOS).
Widget SubscriptionWidget with auto-state (offerings, eligibility, statuses).
App-Wide Singleton-style client for any screen.
Debugging Callbacks + SDK toggles.

Exports SDK types like Offerings, Package, CustomerInfo.

Installation

  1. Add Dependency:

    dependencies:
      revenuecat_subscription: ^1.0.1
    

    Run flutter pub get.

  2. Platform Setup (from RevenueCat Docs):

    • iOS: Set deployment target ≥11.0 in Podfile; enable In-App Purchase in Xcode Capabilities; use Swift ≥5.0.
    • Android: Add <uses-permission android:name="com.android.vending.BILLING" /> in AndroidManifest.xml; set launchMode="standard" or "singleTop" for your Activity.
    • Web (Beta): Use purchases_flutter: ^9.0.0-beta.3 (package pins latest).
  3. RevenueCat Dashboard:

    • Create public API keys (iOS: appl_xxx, Android: goog_xxx).
    • Set up Products (e.g., monthly: $9.99, yearly: $99.99) and Entitlements (e.g., premium).
    • Create Offerings (e.g., "default" with monthly/yearly packages).

See RevenueCat Flutter Install for details.

Quick Start

1. Configure in main.dart

import 'package:flutter/material.dart';
import 'package:revenuecat_subscription/revenuecat_subscription.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Configure once
  await RevenueCatClient.configure(
    iosApiKey: 'appl_your_ios_key',
    androidApiKey: 'goog_your_android_key',
    logLevel: LogLevel.debug, // Optional
  );

  // Optional: Global logging
  RevenueCatClient.onLog = (msg) => debugPrint('[RevenueCat] $msg');
  RevenueCatClient.onError = (err, stack) => debugPrint('Error: $err');

  runApp(MyApp());
}

2. Use Widget for Paywalls (Easiest)

Wrap your paywall screen:

// paywall_screen.dart
import 'package:flutter/material.dart';
import 'package:revenuecat_subscription/revenuecat_subscription.dart';

class PaywallScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SubscriptionWidget(
        userId: 'optional_user_123', // For identified users
        entitlementId: 'premium', // Optional: Specific check
        onSubscribed: () => Navigator.pop(context), // Success callback
        builder: (context, state) {
          if (state.isLoading) return const Center(child: CircularProgressIndicator());
          if (state.error != null) return Center(child: Text('Error: ${state.error}'));

          if (state.hasActiveSubscription) {
            return const Center(child: Text('Premium Unlocked! 🎉'));
          }

          // Paywall UI
          final offering = state.offerings?.current;
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Unlock Premium', style: TextStyle(fontSize: 24)),
              const SizedBox(height: 20),
              // Packages with trial badges & status
              ...?offering?.availablePackages.map((pkg) {
                final eligible = state.eligibility?[pkg.storeProduct.identifier] == IntroEligibility.introPriceOrTrial;
                final status = state.subscriptionStatuses?[pkg.storeProduct.identifier] ?? SubscriptionStatus.inactive;
                return Card(
                  child: ListTile(
                    title: Text(pkg.storeProduct.title),
                    subtitle: Text('${pkg.storeProduct.priceString} - Status: $status'),
                    trailing: eligible ? const Icon(Icons.local_offer, color: Colors.green) : null,
                    onTap: () => state.purchasePackage(pkg),
                  ),
                );
              }),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: state.restorePurchases,
                child: const Text('Restore Purchases'),
              ),
              Text(offering?.metadata['footer'] ?? 'Terms apply.'), // From dashboard
            ],
          );
        },
      ),
    );
  }
}
  • How it Works: Auto-fetches offerings/customerInfo, listens for updates, shows eligibility badges (iOS), and handles purchases/restores.

3. Use Client Directly (App-Wide)

For checks anywhere (e.g., nav guards):

// global_revenuecat.dart
final globalClient = RevenueCatClient(); // Singleton pattern

// In a screen (e.g., home.dart)
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<bool>(
        future: globalClient.hasActiveEntitlement('premium'),
        builder: (context, snapshot) {
          if (snapshot.hasData && snapshot.data == true) {
            return const PremiumDashboard(); // Premium UI
          }
          return Column(
            children: [
              const Text('Free Tier'),
              ElevatedButton(
                onPressed: () async {
                  final offerings = await globalClient.getOfferings();
                  final pkg = offerings.current?.availablePackages.first;
                  if (pkg != null) {
                    try {
                      await globalClient.purchasePackage(pkg);
                      // Handle success (e.g., refresh UI)
                    } catch (e) {
                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Purchase failed: $e')));
                    }
                  }
                },
                child: const Text('Upgrade to Premium'),
              ),
            ],
          );
        },
      ),
    );
  }
}

Usage Examples & Use Cases

Use Case 1: Freemium App (Basic Subscriptions)

  • Scenario: Free app with premium features (e.g., ad-free, unlimited access).
  • Implementation:
    • Use SubscriptionWidget in a paywall modal.
    • Check hasActiveEntitlement('premium') to gate features.
    • Example: In main.dart, wrap routes with a guard:
      // Route to premium if subscribed
      if (await globalClient.hasActiveSubscription()) {
        Navigator.pushNamed(context, '/premium');
      } else {
        Navigator.pushNamed(context, '/paywall');
      }
      
  • Pro Tip: Use customerInfoStream for real-time unlocks after purchase.

Use Case 2: Multi-Tier Subscriptions (Entitlements)

  • Scenario: Monthly ($4.99), Yearly ($49.99), Lifetime ($99) tiers.
  • Implementation:
    • Dashboard: Products → Entitlements (pro, lifetime).
    • Code: Fetch statuses for tiers:
      final statuses = await globalClient.getSubscriptionStatuses(['monthly', 'yearly']);
      if (statuses['yearly'] == SubscriptionStatus.active) {
        // Show yearly perks
      }
      
  • Example Paywall: Customize widget builder to highlight best value (e.g., yearly with trial badge).

Use Case 3: Trial-Driven Onboarding (iOS Focus)

  • Scenario: Show "7-Day Free Trial" only for eligible users.
  • Implementation:
    • In widget: state.eligibility?['monthly'] == IntroEligibility.introPriceOrTrial.
    • Code:
      final eligibility = await globalClient.checkTrialOrIntroEligibility(['monthly']);
      if (eligibility['monthly'] == IntroEligibility.introPriceOrTrial) {
        // Badge: "Try Free for 7 Days"
      }
      
  • Cross-Platform Note: Falls back gracefully on Android.

Use Case 4: User Analytics & Management

  • Scenario: Track sign-ups, set custom attributes (e.g., source: 'google').
  • Implementation:
    // Login on auth
    await globalClient.logIn('user_123');
    await globalClient.setAttributes({'plan': 'pro', 'source': 'organic'});
    
    // Check user ID
    final userId = await globalClient.getAppUserId();
    
  • Restore/Sync: Add "Restore" button; call restorePurchases() or syncPurchases() post-web buy.

Use Case 5: Ad-Free Upgrade (with AdMob)

  • Scenario: Remove ads on subscribe (integrate with AdMob).
  • Implementation (from RevenueCat Blog):
    • On hasActiveSubscription() true: MobileAds.instance.updateRequestConfiguration(...) to disable ads.
    • Example: Listen to stream:
      globalClient.customerInfoStream.listen((info) {
        if (info.entitlements.active.isNotEmpty) {
          // Pause AdMob interstitials
        }
      });
      

Advanced: Real-Time Updates

// In provider or init
globalClient.customerInfoStream.listen((info) {
  final isPro = info.entitlements.all['premium']?.isActive ?? false;
  // Update app state (e.g., Riverpod notifier)
});

Troubleshooting

Issue Solution
No Offerings Verify dashboard products/entitlements match keys. Enable debug: setDebugLogsEnabled(true).
Purchase Fails Check launchMode (Android); test sandbox. Logs via onError.
Trials Not Showing iOS-only; ensure products have intro pricing in App Store Connect.
Cache Stale Use getCustomerInfoFresh() or invalidateCustomerInfoCache().
Web Support Beta: Pin purchases_flutter: ^9.0.0-beta.3; use Stripe for web buys.

Full logs: Set onLog/onError. Test on devices (emulators flaky for billing).

Contributing

  • Fork, PR with tests (use mockito for mocks).
  • Run dart analyze && flutter test.
  • Focus: Simplicity, cross-platform.

License

MIT. See LICENSE.


Inspired by RevenueCat Flutter Tutorial and Official Docs.

Details
Pub
2025-12-17 06:05:13 +00:00
14
13 KiB
Assets (1)
1.0.1.tar.gz 13 KiB
Versions (2) View all
1.0.1 2025-12-17
1.0.0 2025-12-15