subscription_guard 0.0.1
subscription_guard: ^0.0.1 copied to clipboard
A declarative, provider-agnostic subscription tier gating package for Flutter. Gate features, widgets, and routes based on subscription tiers with zero purchase SDK dependency.
subscription_guard #
A declarative, provider-agnostic subscription tier gating package for Flutter.
Stop writing if/else for every premium feature. Gate any widget, route, or feature with one line of code.
The Problem #
// This is scattered across your entire codebase ๐ฉ
if (user.subscription == 'pro' || user.subscription == 'premium') {
return AdvancedStatsWidget();
} else {
return UpgradePrompt();
}
// And again here... and in your routes... and in your navigation...
// 35+ features? That's 35+ if/else blocks. Good luck maintaining that.
The Solution #
// One line. Done. โ
SubscriptionGuard(
requiredTier: 'pro',
child: AdvancedStatsWidget(),
)
โจ Features #
- ๐ก๏ธ Declarative Gating โ
SubscriptionGuardwidget for tier, feature, or specific-tier gating - ๐จ 4 Guard Behaviors โ Hide, Disable, Blur, or Replace locked content
- ๐งญ Route Protection โ Guard entire screens with navigation helpers
- โฑ๏ธ Trial Support โ Built-in trial countdown with
TrialBanner - ๐ Analytics Callbacks โ Track which features users hit paywalls on
- ๐ Provider Agnostic โ Works with RevenueCat, Adapty, or any purchase SDK
- ๐ Debug Overlay โ Draggable tier switcher for testing during development
- ๐ฆ Zero Dependencies โ Pure Flutter, no external packages
- ๐ฏ Feature Registry โ Map features to tiers centrally, reference by ID
- ๐ก Programmatic Access โ Check tier access anywhere in code
๐ฆ Installation #
Add to your pubspec.yaml:
dependencies:
subscription_guard: ^0.0.1
Then run:
flutter pub get
๐ Quick Start #
Step 1: Define your tiers #
final config = SubscriptionConfig(
tiers: [
Tier(id: 'free', level: 0, label: 'Free'),
Tier(id: 'pro', level: 1, label: 'Pro'),
Tier(id: 'premium', level: 2, label: 'Premium'),
],
features: {
'advanced_stats': 'pro',
'export_pdf': 'pro',
'team_management': 'premium',
},
);
Step 2: Wrap your app #
SubscriptionGuardProvider(
config: config,
currentTier: 'free', // Update this when user purchases
onUpgradeRequested: (requiredTier) {
// Show your paywall (RevenueCat, Adapty, etc.)
showPaywall(requiredTier);
},
child: MyApp(),
)
Step 3: Guard any widget #
SubscriptionGuard(
requiredTier: 'pro',
child: AdvancedStatsWidget(),
)
That's it! The widget automatically shows or hides based on the user's tier. When the tier changes, all guards update instantly.
๐ Usage #
Guard Behaviors #
// Hide โ completely removes from widget tree
SubscriptionGuard(requiredTier: 'pro', behavior: GuardBehavior.hide, child: ProWidget())
// Disable โ visible but greyed out and non-interactive
SubscriptionGuard(requiredTier: 'pro', behavior: GuardBehavior.disable, child: ProWidget())
// Replace โ shows locked UI (default behavior)
SubscriptionGuard(requiredTier: 'pro', behavior: GuardBehavior.replace, child: ProWidget())
// Blur โ shows blurred preview with adaptive lock overlay
SubscriptionGuard(requiredTier: 'pro', behavior: GuardBehavior.blur, child: ProWidget())
Feature-Based Gating #
// Define features in config, reference by ID
SubscriptionGuard.feature(
featureId: 'export_pdf',
child: ExportButton(),
)
Specific Tier Gating (Non-Hierarchical) #
// Only 'pro' users โ NOT premium, NOT free
SubscriptionGuard.allowedTiers(
tierIds: ['pro'],
child: ProExclusiveBadge(),
)
Custom Locked Widget #
SubscriptionGuard(
requiredTier: 'premium',
lockedBuilder: (context, requiredTier, currentTier) {
return MyCustomPaywall(tier: requiredTier);
},
child: PremiumFeature(),
)
Navigation Guards #
// Method 1: pushGuarded โ blocks navigation if tier insufficient
SubscriptionRouteGuard.pushGuarded(
context,
requiredTier: 'pro',
route: MaterialPageRoute(builder: (_) => AnalyticsScreen()),
onBlocked: (required, current) => showUpgradeDialog(required),
);
// Method 2: SubscriptionPageRoute โ always pushes, shows locked UI if blocked
Navigator.of(context).push(
SubscriptionPageRoute(
requiredTier: 'pro',
builder: (_) => AnalyticsScreen(),
),
);
// Method 3: GoRouter compatible redirect (no go_router dependency required)
GoRoute(
path: '/analytics',
redirect: subscriptionRedirect(
requiredTier: 'pro',
redirectPath: '/upgrade',
),
)
Programmatic Access Check #
final scope = SubscriptionGuardScope.of(context);
if (scope.hasAccess('pro')) {
// Do something for pro users
}
if (scope.hasFeatureAccess('export_pdf')) {
// Feature is accessible
}
// Or use the route guard utility
final result = SubscriptionRouteGuard.checkAccess(context, requiredTier: 'pro');
if (result.hasAccess) { /* ... */ }
Trial Support #
SubscriptionGuardProvider(
config: config,
currentTier: 'pro',
trialInfo: TrialInfo(
isTrialing: true,
endsAt: DateTime.now().add(Duration(days: 7)),
),
child: Column(
children: [
TrialBanner(onTap: () => showUpgradeDialog()),
SubscriptionGuard(
requiredTier: 'pro',
allowDuringTrial: true, // default
child: ProFeature(),
),
],
),
)
Analytics / Tracking #
SubscriptionGuardProvider(
config: config,
currentTier: 'free',
onFeatureBlocked: (featureId, requiredTier, currentTier) {
analytics.track('feature_gated', {
'feature': featureId,
'required_tier': requiredTier.id,
'current_tier': currentTier.id,
});
},
onUpgradeRequested: (requiredTier) {
analytics.track('upgrade_requested', {'tier': requiredTier.id});
},
child: MyApp(),
)
Debug Overlay #
SubscriptionGuardDebugOverlay(
enabled: kDebugMode, // Auto-disabled in release builds
onTierChanged: (tierId) {
setState(() => _currentTier = tierId);
},
child: MyApp(),
)
๐ Works With Any Purchase SDK #
subscription_guard doesn't handle purchases โ it only handles UI gating. Pair it with your preferred purchase SDK:
RevenueCat:
Purchases.addCustomerInfoUpdateListener((info) {
final tier = info.entitlements.active.containsKey('premium')
? 'premium'
: info.entitlements.active.containsKey('pro') ? 'pro' : 'free';
setState(() => _currentTier = tier);
});
Adapty:
Adapty.getProfile().then((profile) {
final tier = profile.accessLevels['premium']?.isActive == true
? 'premium' : 'free';
setState(() => _currentTier = tier);
});
Raw in_app_purchase:
InAppPurchase.instance.purchaseStream.listen((purchases) {
setState(() => _currentTier = deriveTierFromPurchases(purchases));
});
Just update currentTier on the provider โ subscription_guard handles the rest.
๐๏ธ Architecture #
subscription_guard uses a pure InheritedWidget architecture โ no BLoC, Riverpod, or Provider dependency. Your purchase SDK feeds the current tier into SubscriptionGuardProvider, which propagates it down the widget tree. Every SubscriptionGuard, SubscriptionRouteGuard, and TrialBanner reacts automatically when the tier changes.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Purchase SDK โ
โ (RevenueCat / Adapty / etc.) โ
โ โ โ
โ โผ โ
โ SubscriptionGuardProvider(currentTier) โ
โ โ โ
โ โโโโโโโโโโโโผโโโโโโโโโโโ โ
โ โผ โผ โผ โ
โ SubscriptionGuard RouteGuard TrialBanner โ
โ โ โ โ โ
โ โโโโโโดโโโโโ Access Countdown โ
โ โ Access? โ Check โ
โ โโYesโโบShowโ โ
โ โโNoโโโบLockโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ API Reference #
Full API documentation is available at: pub.dev/documentation/subscription_guard
Key Classes #
| Class | Description |
|---|---|
SubscriptionGuardProvider |
Root widget that provides tier state to the tree |
SubscriptionGuard |
Core gating widget โ tier, feature, or allowedTiers |
SubscriptionConfig |
Configuration for tiers and feature mappings |
Tier |
Represents a single subscription tier |
GuardBehavior |
Enum: hide, disable, replace, blur |
TrialInfo |
Trial period state |
TrialBanner |
Trial countdown banner widget |
DefaultLockedWidget |
Built-in locked state UI |
SubscriptionRouteGuard |
Static navigation guard utilities |
SubscriptionPageRoute |
MaterialPageRoute with built-in tier check |
RouteAccessResult |
Result of a programmatic access check |
SubscriptionGuardDebugOverlay |
Debug tier switcher overlay |
SubscriptionGuardScope |
Programmatic access to subscription state |
๐ฎ Example #
Check out the example app for a complete demo of all features.
cd example
flutter create . # Generate platform files (first time only)
flutter run
The example app includes:
- Tier switching simulation
- All 4 guard behaviors
- Feature-based gating
- Navigation guards
- Trial banner
- Debug overlay
- Custom locked builders
- Analytics callback logging
๐ค Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please make sure to:
- Update tests as appropriate
- Follow existing code style
- Add documentation for new features
- Run
dart analyzeandflutter testbefore submitting
๐ License #
This project is licensed under the MIT License โ see the LICENSE file for details.
Built with โค๏ธ by YOUR_NAME
If this package helps you, please โญ the repo and ๐ on pub.dev!