App Paywall
Learn how to set up, configure, and personalize the built-in paywall for your Floot app.
After setting up Stripe or RevenueCat, any plans you create will automatically appear on the paywall page. Below are instructions on how to further customize the paywall and add extra features.
Lifetime Subscriptions
Create Lifetime Price
By default, the paywall displays only recurring plans. If you want to offer a lifetime deal, create a one-time payment offer in your payment provider (Stripe or RevenueCat) following the Floot guides. After creating the offer, it will appear in your Supabase prices
table with the interval
column set to NULL
(the default for non-recurring prices). To display it on the paywall, update the interval
value to lifetime
by executing the following SQL command in your Supabase SQL editor:
UPDATE prices
SET interval = 'lifetime'
WHERE id = 'lifetime_price_id';
Handle server-side logic
After configuring lifetime prices, implement the server-side logic to process one-time payments for Stripe or RevenueCat.
Below is an example of creating a subscription that remains active for 100 years.
const subscriptionData: TablesInsert<"subscriptions"> = {
id: uuidv4(),
user_id: user.id,
price_id: "lifetime_price_id",
status: "active",
current_period_start: new Date().toISOString(),
// add 100 years to the current date
current_period_end: new Date(
new Date().setFullYear(new Date().getFullYear() + 100),
).toISOString(),
cancel_at_period_end: false,
};
const { error } = await supabase.from("subscriptions").insert(subscriptionData);
if (error) {
console.error(
`DB: Error creating subscription ${subscriptionData.id}:`,
error.message,
);
throw error;
}
return console.info(
`🎉 DB: Lifetime subscription created successfully: ${subscriptionData.id}, for user with email: ${user.email}`,
);
Plans descriptions
By default, plan descriptions are taken from your payment provider. To customize these descriptions, for example, to support localization, provide the descriptions
map in the PriceListView
widget of your PaywallPage
. Note: Keys are case sensitive.
Example with two plans named Basic and Premium:
const PriceListView(
descriptions: <String, String>{
'Basic': 'Perfect for getting started.',
'Premium': 'Best for growing startups and growth companies.',
},
)
Plans features
To display the features included in each plan, pass a features
map to the PriceListView
widget in your PaywallPage
. Note: The keys must exactly match your plan names (case sensitive).
Example with two plans named Basic and Premium:
const PriceListView(
features: <String, List<FeatureCard>>{
'Basic': [
FeatureCard(
icon: LucideIcons.timer,
description: 'Access to basic features',
),
FeatureCard(
icon: LucideIcons.lock,
description: 'Secure your account',
),
],
'Premium': [
FeatureCard(
icon: LucideIcons.infinity,
description: 'Unlimited access to all features',
),
FeatureCard(
icon: LucideIcons.zap,
description: 'Premium performance boost',
),
],
},
)
Apps without paywall
If your app does not require a paywall (for example, if it is free to use), you can bypass the paywall screen entirely. To do this, update the postAuthPath
parameter in the AuthRouter.redirect
function within your router_config.dart
file to point to your desired route.
In the example below, users are redirected to the dashboard immediately after authentication.
GoRouter routerConfig(AuthBloc bloc) {
return GoRouter(
...
// * Top level guard
redirect: (context, state) => AuthRouter.redirect(
context,
state,
bloc: bloc,
postAuthPath: (user) => '/dashboard',
),
...
);
}