in_app_subscription_bundle 0.0.12 copy "in_app_subscription_bundle: ^0.0.12" to clipboard
in_app_subscription_bundle: ^0.0.12 copied to clipboard

A plugin to make in app purchase easy.

in_app_subscription_bundle #

A powerful, reusable, BLoC-based Flutter plugin for managing In-App Purchases (IAP) and Subscriptions on Android and iOS without requiring a backend server.

It dynamically fetches product details, handles purchase streams, parses subscription billing periods via native Google Play & Apple App Store APIs, and supports both consumable (e.g., coins, credits) and non-consumable (e.g., subscriptions, permanent unlocks) products.


Features #

  • Backend-Less Architecture: Works 100% offline using native store APIs.
  • Subscriptions & Non-Consumables: Auto-renewable subscriptions, one-time purchases, upgrade/downgrade logic.
  • Consumable Products: Auto-consuming support with onConsumablePurchased callback to credit local inventories.
  • Dynamic Duration Parsing: Automatically reads billing periods from Google Play (ISO 8601) and Apple StoreKit at runtime.
  • Robust Error & Partial Load Handling: Successfully loads available store products even if some IDs are pending or missing.
  • Flutter BLoC Integration: Clean state management with SubsBlocNew and SubscriptionState.

Installation #

Add in_app_subscription_bundle to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  in_app_subscription_bundle: ^0.0.12

Usage Guide #

Step 1: Import the Package #

import 'package:in_app_subscription_bundle/in_app_subscription_bundle.dart';

Step 2: Initialize SubsBlocNew #

Wrap your subscription/store screen with BlocProvider:

class ProductsView extends StatelessWidget {
  const ProductsView({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => SubsBlocNew(
        context: context,
        subscriptionProductIds: ["monthly_plan", "yearly_plan"], // Subscriptions & Non-Consumables
        consumableProductIds: ["100_coins", "500_coins"],         // Consumable Product IDs
        isSandbox: kDebugMode,                                   // Uses sandbox in debug mode
        sharedSecret: "YOUR_IOS_APP_SPECIFIC_SHARED_SECRET",     // Optional: iOS App Store Connect shared secret
        onConsumablePurchased: (purchaseDetails) {
          // Triggered when a consumable product is successfully purchased.
          // Credit your local inventory (e.g. Hive, SharedPreferences, SQLite) here.
          AppLogs.showInfoLogs("Consumable Purchased: ${purchaseDetails.productID}");
        },
      )..add(SubscriptionInitEvent(context: context)),
      child: const _ProductsBody(),
    );
  }
}

Step 3: Build the UI #

Use BlocBuilder to react to state changes and trigger events:

class _ProductsBody extends StatelessWidget {
  const _ProductsBody({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<SubsBlocNew, SubscriptionState>(
      builder: (context, state) {
        final bloc = context.read<SubsBlocNew>();

        return Scaffold(
          appBar: AppBar(
            title: const Text("In-App Store"),
          ),
          body: state.loader == true
              ? const Center(child: CircularProgressIndicator())
              : SingleChildScrollView(
                  child: Column(
                    children: [
                      // Product List
                      ListView.builder(
                        itemCount: state.products.length,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (_, index) {
                          final product = state.products[index];
                          if (product.rawPrice == 0.0) return const SizedBox.shrink();

                          final isSelected = state.products[state.selectedItem].id == product.id;

                          return ListTile(
                            tileColor: isSelected ? Colors.green.shade100 : null,
                            title: Text(bloc.getValueBeforeBracket(product.title)),
                            subtitle: Text(product.description),
                            trailing: Text(product.price),
                            onTap: () {
                              context.read<SubsBlocNew>().add(
                                ChangeSelectedItemEvent(index, productId: product.id),
                              );
                            },
                          );
                        },
                      ),

                      // Purchase Button
                      ElevatedButton(
                        onPressed: () {
                          context.read<SubsBlocNew>().add(
                            BuyProductEvent(context: context, restore: false),
                          );
                        },
                        child: Text(
                          state.isSubscribed == true ? "Upgrade / Downgrade" : "Buy Product",
                        ),
                      ),

                      // Restore Purchases Button
                      TextButton(
                        onPressed: () {
                          context.read<SubsBlocNew>().add(
                            BuyProductEvent(context: context, restore: true),
                          );
                        },
                        child: const Text("Restore Purchases"),
                      ),
                    ],
                  ),
                ),
        );
      },
    );
  }
}

API Reference #

SubsBlocNew Constructor Parameters #

Parameter Type Required Description
context BuildContext Yes BuildContext for showing notifications/snackbars.
subscriptionProductIds List<String> Yes Product IDs for auto-renewable subscriptions & non-consumables.
consumableProductIds List<String> No Product IDs for consumables (coins, lives, credits). Defaults to [].
isSandbox bool Yes Pass kDebugMode to enable sandbox testing.
sharedSecret String No App Store Connect shared secret for iOS receipt verification.
onConsumablePurchased Function(PurchaseDetails) No Callback fired when a consumable purchase is finalized.

Events #

  • SubscriptionInitEvent(context: context): Initializes store connection and queries product details.
  • BuyProductEvent(context: context, restore: false): Purchases the selected product (or restores if restore: true).
  • ChangeSelectedItemEvent(index, productId: id): Changes selected product index in state.
  • GetOldPurchaseEvent(): Queries store history to compute active subscription status.

State Properties (SubscriptionState) #

  • isSubscribed: bool indicating if the user has an active subscription.
  • products: List of ProductDetails fetched from Google Play & App Store.
  • selectedItem: Index of the currently selected product.
  • subsExpiryDate: Calculated expiry date string for active subscriptions.
  • loader: bool indicating loading state.

License #

MIT License.