kitty_sdk 0.11.0
kitty_sdk: ^0.11.0 copied to clipboard
Kitty Flutter SDK — unified package for device fingerprint attribution (Flare) and authentication, user management, licenses & subscriptions (Account) against the Kitty Launch platform APIs.
Kitty Flutter SDK #
Unified Flutter SDK for the Kitty Launch platform — combines Flare (device fingerprint attribution) and Account (authentication, user management, licenses, subscriptions) in a single package.
Install #
dependencies:
kitty_sdk: ^0.9.0
Quick Start #
import 'package:kitty_sdk/kitty_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await KittySdk.initialize(
config: KittySdkConfig(
projectId: 'proj_xxx',
accountBaseUrl: '<your-account-api-url>',
flareBaseUrl: '<your-flare-api-url>',
),
);
runApp(const MyApp());
}
Usage #
Flare — fingerprint identification #
final match = await KittySdk.flare.identify();
if (match != null) {
print('Matched! uid=${match.uid} type=${match.matchType}');
}
Account — authentication #
final account = await KittySdk.account.loginWithPassword(
email: 'user@example.com',
password: 'secret',
);
KittySdk.account.accountStream.listen((account) {
// React to auth state changes
});
Account — licenses #
final user = KittySdk.account.currentAccount;
final license = user?.bestLicense;
if (license != null && license.isActive) {
// User has active license
}
Account UI — theming #
Set the base SDK UI theme once:
await KittySdk.initialize(
config: KittySdkConfig(
projectId: 'proj_xxx',
accountBaseUrl: '...',
flareBaseUrl: '...',
theme: KittyTheme(
themeData: ThemeData(
useMaterial3: true,
fontFamily: 'DM Sans',
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
),
),
),
);
Customize auth and bind-email screens separately:
final authTheme = KittyAuthTheme(
backgroundColor: Colors.white,
loginHeaderWidget: Image.asset('assets/logo.png', height: 64),
codeHeaderWidget: Image.asset('assets/logo.png', height: 48),
loginBodyPadding: EdgeInsets.symmetric(horizontal: 24),
primaryButtonStyle: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 48),
shape: StadiumBorder(),
),
inputDecoration: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
),
);
KittyAuthFlow(
account: KittySdk.account,
theme: authTheme,
);
KittyBindEmailFlow(
account: KittySdk.account,
theme: authTheme,
);
Customize settings screens by logical blocks:
KittySettingsFlow(
account: KittySdk.account,
onLoginPressed: () {},
onRenewSubscription: () {},
theme: KittySettingsTheme(
page: KittySettingsPageTheme(
backgroundColor: Colors.white,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
),
menuItem: KittySettingsMenuItemTheme(
borderRadius: BorderRadius.all(Radius.circular(16)),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 14),
iconColor: Colors.black54,
),
buttons: KittySettingsButtonsTheme(
primaryButtonSize: Size(240, 52),
primaryButtonStyle: ElevatedButton.styleFrom(
shape: StadiumBorder(),
),
),
dialog: KittySettingsDialogTheme(
destructiveActionStyle: TextStyle(color: Colors.red),
),
bindEmail: KittySettingsBindEmailTheme(
authTheme: authTheme,
),
),
);
All theme fields are optional. If a field is null, the SDK uses its default style from Theme.of(context) / the built-in Kitty UI.
Buy Now — web paywall with mobile prices #
Requires Common Module v13.4.0+ on the web side.
final products = <ProductDetails>{...}; // from InAppPurchase
router.push(MaterialPageRoute(
builder: (_) => KittyWebBuyNowPage(
url: 'https://your-domain.com/buy-now', // Kitty Flow url
products: products,
onPaymentSuccess: () {
router.pop();
},
),
));
Modules #
| Module | Access | Description |
|---|---|---|
| Flare | KittySdk.flare |
Device fingerprint identification & purchase attribution |
| Account | KittySdk.account |
Auth, user management, licenses, subscriptions |
| Tokens | KittySdk.tokens |
Token balance and operations (requires Account) |
| Buy Now | KittySdk.buyNow |
Web paywall with mobile price bridging |
Tokens — balance and operations #
// 1. Enable tokens in KittySdkConfig
await KittySdk.initialize(
config: KittySdkConfig(
projectId: 'proj_xxx',
accountBaseUrl: '...',
flareBaseUrl: '...',
tokens: const KittyTokensConfig(isDebug: true),
),
);
// 2. Use the module (user must be logged in via KittySdk.account)
// By default the SDK uses the first usable license of the current user.
// Pass `licenseId:` explicitly when you need to target a specific license.
final balance = await KittySdk.tokens.getBalance();
print('Tokens: ${balance.balance}');
// Get operations history
final ops = await KittySdk.tokens.getOperations(skip: 0, limit: 20);
// Token mutations (add/remove) are server-side only — call them from your
// backend with an internal API key or a per-project `kap_` bearer token
// against `POST /api/internal/tokens/add` and `/api/internal/tokens/remove`.
// 3. Use the Cubit in your UI
BlocProvider(
create: (_) => TokensCubit(tokens: KittySdk.tokens)..loadBalance(),
child: BlocBuilder<TokensCubit, TokensState>(
builder: (context, state) {
if (state.isLoading) return const CircularProgressIndicator();
return Text('Balance: ${state.tokenCount}');
},
),
)
Backend token mutations — important notes
add/remove are server-side only (call them from your backend with a per-project
kap_ bearer token). Before you build against them, mind these three behaviours
(full guide: kitty-launch-account/docs/backend-token-api.md):
/addand/removehit DIFFERENT buckets./addcredits the persistentbalancebucket;/removedebits the renewablesubscriptionBalancebucket (which has no floor and can go negative). A naive "grant then spend" will not net out — for a "tokens per operation" model, confirm with the platform team which bucket you should be debiting.- No idempotency on
/add//remove. Calling twice applies twice. Deduplicate on your side (fire exactly once per operation, e.g. guard by your own operation id). - A
kap_token cannot read balance.GET /balanceandGET /operationsrequire the internal API key — akap_bearer gets 401 there. Decide how your backend will verify results (e.g. read balance in the client via the SDK, or request read access from the platform team).