flutter_stripe_connect
A Flutter plugin for Stripe Connect embedded components. Easily integrate account onboarding, account management, payouts, payments, and more into your Flutter app.
Features
- Account Onboarding - Collect connected account information with a pre-built UI
- Account Management - Let connected accounts manage their account settings
- Payments - Show payment history for connected accounts
- Payouts - Display payout history and status for connected accounts
- Balances - Show balance information and payout controls
- Notification Banner - Display required actions for compliance
- Documents - Show documents available for download
- Tax Settings - Allow connected accounts to configure tax settings
- Tax Registrations - Manage tax registrations
- Disputes List - View and manage disputes
- Payment Details - Show detailed payment information
- Payout Details - Show detailed payout information
- Payouts List - Filterable list of payouts
- WebView Mode - Optional self-hosted web rendering for full component access
- Customizable Appearance - Configure colors, fonts, and corner radius
Platform Support
| Platform | Supported |
|---|---|
| Android | ✅ |
| iOS | ✅ |
| Web | ✅ |
Component Availability by Platform
| Component | iOS Native | Android Native | Web | Mobile WebView |
|---|---|---|---|---|
| Account Onboarding | ✅ | ✅ | ✅ | Optional |
| Payments | ✅ | ✅ | ✅ | Optional |
| Payouts | ✅ | ✅ | ✅ | Optional |
| Account Management | ✅ | ❌ | ✅ | Required on Android |
| Notification Banner | ❌ | ❌ | ✅ | Required |
| Balances | ❌ | ❌ | ✅ | Required |
| Documents | ❌ | ❌ | ✅ | Required |
| Tax Settings | ❌ | ❌ | ✅ | Required |
| Tax Registrations | ❌ | ❌ | ✅ | Required |
| Disputes List | ❌ | ❌ | ✅ | Required |
| Payment Details | ❌ | ❌ | ✅ | Required |
| Payout Details | ❌ | ❌ | ✅ | Required |
| Payouts List | ❌ | ❌ | ✅ | Required |
Legend:
- ✅ Native SDK supported - uses platform-native component by default
- ❌ No native SDK - requires WebView mode on mobile
- Optional: Component supports both native and WebView (use
useWebView: trueto force WebView)- Required: Component requires
webViewConfigto work on mobileSee doc/WEBVIEW_INTEGRATION.md for WebView mode setup.
Installation
Add flutter_stripe_connect to your pubspec.yaml:
dependencies:
flutter_stripe_connect: ^0.5.0
Platform Setup
Android Setup
Important: Your MainActivity must extend FlutterFragmentActivity (not FlutterActivity) for the Stripe Connect components to work properly.
Update your android/app/src/main/kotlin/.../MainActivity.kt:
package com.example.yourapp
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()
iOS Setup
Important: add NSCameraUsageDescription to your ios/Runner/Info.plist.
Embedded components capture identity documents with the camera, and the
StripeConnect SDK asserts on this key when the component manager is created —
without it a debug build stops there.
<key>NSCameraUsageDescription</key>
<string>This app uses your camera to take a photo of your identity documents.</string>
The plugin ships both a podspec and a Package.swift, so it works under
CocoaPods and under Swift Package Manager. Either way the app has to target
iOS 15.0 or later.
Troubleshooting: If pod install fails on the first try, run:
cd ios/ pod install --repo-update
Web Setup
Add the Connect.js script to your web/index.html inside the <head> tag:
<script src="https://connect-js.stripe.com/v1.0/connect.js" async></script>
CSP Requirements: If you're using Content Security Policy headers, allow these Stripe domains:
https://connect-js.stripe.comhttps://js.stripe.com
Usage
1. Initialize the SDK
import 'package:flutter_stripe_connect/flutter_stripe_connect.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await StripeConnect.instance.initialize(
publishableKey: 'pk_test_...',
clientSecretProvider: () async {
// Fetch client secret from your server
final response = await http.post(
Uri.parse('https://your-server.com/create-account-session'),
);
return jsonDecode(response.body)['client_secret'];
},
);
runApp(MyApp());
}
1b. Enable WebView Mode (Optional)
For full component access on mobile (including Tax, Capital, Issuing), use WebView mode:
await StripeConnect.instance.initialize(
publishableKey: 'pk_test_...',
clientSecretProvider: () async {
final response = await http.post(
Uri.parse('https://your-server.com/create-account-session'),
);
return jsonDecode(response.body)['client_secret'];
},
webViewConfig: WebViewConfig(
baseUrl: 'https://connect.yourapp.com', // Your hosted web app
theme: 'light',
primaryColor: '#635BFF',
// Optional: Customize URL parameter names if your web app uses different names
// publishableKeyParam: 'pk', // defaults to 'publishableKey'
// clientSecretParam: 'secret', // defaults to 'clientSecret'
),
);
Note: WebView mode requires hosting your own Next.js app. See doc/WEBVIEW_INTEGRATION.md for setup guide.
2. Use the Embedded Components
Account Onboarding
Option A: Embed as Widget
StripeAccountOnboarding(
onLoaded: () => print('Onboarding loaded'),
onLoadError: (error) => print('Error: $error'),
onExit: () => print('User exited onboarding'),
// Uses native SDK by default on iOS/Android
// Set useWebView: true to force WebView rendering
)
Option B: Present Programmatically (New in 0.3.0)
Trigger onboarding from your own UI without embedding the widget:
ElevatedButton(
onPressed: () async {
await StripeConnect.presentAccountOnboarding(
onExit: () {
print('User exited onboarding');
// Navigate back or refresh state
},
onLoadError: (error) {
print('Error: $error');
// Show error dialog
},
);
},
child: Text('Start Onboarding'),
)
Note:
presentAccountOnboarding()is only supported on iOS and Android. On Web, use theStripeAccountOnboardingwidget.
Collecting more than what is currently due
Onboarding always collects currently_due requirements. Anything Stripe defers
is left out — for a US individual, the date of birth and the last four digits
of the social security number are only due as the account approaches its first
payouts, so an onboarding run early in your flow never asks for them. Pass
collectionOptions to pull them forward:
await StripeConnect.presentAccountOnboarding(
collectionOptions: const AccountCollectionOptions(
fields: AccountFieldOption.eventuallyDue,
futureRequirements: AccountFutureRequirementOption.include,
),
);
Collecting more than what is currently due is subject to Stripe's policy instructions.
Your own agreements, and your own title
For connected accounts where your platform is responsible for collecting requirements, you can put your own agreements in place of Stripe's links and take terms acceptance through your own flow:
await StripeConnect.presentAccountOnboarding(
title: context.l10n.onboardingTitle,
fullTermsOfServiceUrl: 'https://example.com/terms',
recipientTermsOfServiceUrl: 'https://example.com/recipient-terms',
privacyPolicyUrl: 'https://example.com/privacy',
collectionOptions: const AccountCollectionOptions(
excludeTermsOfService: true,
),
);
Both sets of options are also available on the StripeAccountOnboarding
widget. title applies to native platforms only — on web the component has no
title bar of its own. Excluding terms acceptance hides it from onboarding; your
platform must collect and record acceptance separately. On Android, the native
SDK does not expose requirement restrictions to plugin callers, so the plugin
uses the equivalent terms collection flag. The older
skipTermsOfServiceCollection argument remains available but is deprecated.
Account Management
StripeAccountManagement(
onLoaded: () => print('Account management loaded'),
onLoadError: (error) => print('Error: $error'),
)
Payments
StripePayments(
onLoaded: () => print('Payments loaded'),
onLoadError: (error) => print('Error: $error'),
)
Payouts
StripePayouts(
onLoaded: () => print('Payouts loaded'),
onLoadError: (error) => print('Error: $error'),
)
Balances
StripeBalances(
onLoaded: () => print('Balances loaded'),
onLoadError: (error) => print('Error: $error'),
)
Tax Settings (Web Only)
StripeTaxSettings(
onLoaded: () => print('Tax settings loaded'),
onLoadError: (error) => print('Error: $error'),
)
Disputes List
StripeDisputesList(
onLoaded: () => print('Disputes loaded'),
onLoadError: (error) => print('Error: $error'),
)
3. Customize Appearance (Optional)
Appearance belongs to the Stripe instance rather than to an individual component, so it is set once at initialization and applies everywhere:
await StripeConnect.instance.initialize(
publishableKey: 'pk_test_...',
clientSecretProvider: fetchClientSecret,
appearance: const ConnectAppearance(
fontFamily: 'Roboto',
cornerRadius: 12.0,
colors: ConnectColors(
primary: '#635BFF',
background: '#FFFFFF',
text: '#1A1A1A',
),
),
);
Restyle later — following the app's theme, for instance — with
updateAppearance, which also reaches the components already on screen:
await StripeConnect.instance.updateAppearance(
const ConnectAppearance(colors: ConnectColors(background: '#111111')),
);
Colors are hex strings. On native platforms #RGB and #RRGGBB are accepted;
alpha is not, because CSS reads #RRGGBBAA while Android reads #AARRGGBB.
cornerRadius is a base radius in pixels.
Fonts
fontFamily reaches only as far as naming a family. Every embedded component
renders in a web context — a WKWebView on iOS, a WebView on Android, an
iframe on web — and that context does not inherit the fonts of the app around
it. Each SDK therefore takes the font file through a channel of its own:
EmbeddedComponentManager(fonts:) on iOS, customFonts on Android, the
fonts option of loadConnectAndInitialize on web. The plugin does not
expose any of the three, so a font your app bundles will not render. Stay
with a system family, and check it on each platform you ship: iOS, Android and
a desktop browser do not carry the same ones. The Roboto above is an Android
system font.
iOS differs in one respect: the family is resolved through UIFont(name:)
before it is handed to the SDK, so the generic CSS families (sans-serif,
serif, monospace) that Android and web accept are rejected there and fall
back to -apple-system. Name a concrete font rather than a generic family.
Note: the deprecated
appearanceargument on individual component widgets has no effect. Set it throughinitializeorupdateAppearanceinstead.
Server-Side Setup
To use Stripe Connect embedded components, create an Account Session on your server. Authenticate the caller, resolve their connected account on the server, and enable only the components and features their role permits. This Node.js example illustrates the component configuration; supply your own authentication and account lookup:
const stripe = require('stripe')('sk_test_...');
app.post('/create-account-session', async (req, res) => {
const connectedAccountId = await getConnectedAccountIdForAuthenticatedUser(req);
const accountSession = await stripe.accountSessions.create({
account: connectedAccountId,
components: {
account_onboarding: { enabled: true },
account_management: { enabled: true },
payments: {
enabled: true,
features: {
refund_management: true,
dispute_management: true,
capture_payments: true,
}
},
payouts: {
enabled: true,
features: {
instant_payouts: true,
standard_payouts: true,
}
},
balances: { enabled: true },
tax_settings: { enabled: true },
tax_registrations: { enabled: true },
documents: { enabled: true },
notification_banner: { enabled: true },
},
});
res.json({ client_secret: accountSession.client_secret });
});
Requirements
- Flutter SDK
>=3.44.0 - Dart SDK
>=3.12.0 <4.0.0 - Android:
minSdk 24,compileSdk 36 - iOS:
iOS 15.0+ - Web: Modern browsers (Chrome, Firefox, Safari, Edge)
Documentation
- WebView Integration Guide - How to set up WebView mode for full component access
- Authentication Flow - Understanding the Stripe Connect authentication flow
- SDK Research - Technical research notes on the Stripe Connect SDKs
License
MIT License - see LICENSE for details.
Libraries
- flutter_stripe_connect
- Flutter Stripe Connect - A Flutter plugin for Stripe Connect embedded components
- flutter_stripe_connect_web
- Web plugin registration for Flutter Stripe Connect