ctrlapp 0.1.2
ctrlapp: ^0.1.2 copied to clipboard
Remote kill-switch, feature flags, forced update, forms and inbox for Flutter, plus diagnostics, device-integrity checks, push permission and hosted login.
ctrlapp — remote control for your Flutter app #
Change how your live app behaves from the CtrlApp dashboard — without publishing a new version.
Turn off a broken feature, show a maintenance screen, force an update, change a button label, or run an experiment, and your users' apps pick it up automatically, usually within a minute.
What you can do #
| You want to... | You use |
|---|---|
| Switch off the whole app, or one broken feature, instantly | the kill-switch (isBlocked, boolFlag) |
| Show a "please update" screen to old versions | forced update (blockChanges) |
| Turn features on for 5% of users, then 100% | feature flags (boolFlag) |
| Change numbers, texts, images or links remotely | remote config (configValue, text, image) |
| Test two versions of something (A/B) | experiments (treatment) |
| Collect feedback or sign-ups | forms (submitForm) and an in-app inbox |
| Stop screenshots and screen recording of sensitive screens | screen capture protection (a switch in the dashboard) |
It keeps working offline: the last known decision is cached on the device, so a killed feature stays killed even if the user opens the app without internet.
Get started in 3 steps #
1. Create an app at ctrlapp.dev and copy its API key
(it starts with ck_sdk_).
2. Add the package
flutter pub add ctrlapp
3. Start it when your app launches
import 'package:ctrlapp/ctrlapp.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await CtrlApp.init(apiKey: 'ck_sdk_YOUR_KEY');
runApp(const MyApp());
}
That's it. From anywhere in your app you can now use CtrlApp.instance.
Everyday use #
final ctrl = CtrlApp.instance;
// Kill-switch: should the app (or a feature) be blocked right now?
if (ctrl.isBlocked) {
showMaintenanceScreen(ctrl.blockReason());
}
// React when you flip the switch in the dashboard while the app is open.
ctrl.blockChanges.listen((block) {
if (block.action == 'force_update') openStore(block.forceUpdateUrl);
});
// Feature flag
if (ctrl.boolFlag('new_checkout')) {
showNewCheckout();
}
// Remote config, text and images (with safe defaults)
final maxItems = ctrl.configValue<int>('cart.max_items', defaultValue: 10);
final title = ctrl.text('home.title', fallback: 'Welcome');
final banner = ctrl.image('home.banner'); // null if not set
// Experiment: which version should this user see?
final variant = await ctrl.treatment('checkout_test', defaultValue: 'control');
// Track what people do
ctrl.track('purchase', props: {'plan': 'pro'});
Every call returns the default you pass in if the control doesn't exist, so your app never breaks because a key is missing.
Screen capture protection #
Turn it on per app in the dashboard (it is off by default). The package then applies it automatically:
- Android: blocks screenshots and screen recording of your app (
FLAG_SECURE). It never clears a flag your own code set. - iOS: hides your app's content in the app switcher and covers it while the screen is being recorded or mirrored. Apple provides no way to block a plain screenshot, so iOS cannot prevent those.
More in this package #
Everything is one package. Next to CtrlApp you can use these on their own:
| Need | Use |
|---|---|
| Crash and error reports (opt-in, sensitive data removed) | CtrlAppDiagnostics, CtrlAppHandlers |
| Detect rooted / jailbroken devices and emulators | CtrlAppIntegrity.detect() |
| Device model and OS version | CtrlAppDeviceInfo.detect() |
| Ask for notification permission | CtrlAppPush.requestPermission() |
| Re-check the kill-switch while the app stays open | CtrlAppBlockWatch |
| Sign-in with Google and other providers | package:ctrlapp/auth.dart |
| Test your app without a network | MockCtrlAppTransport |
final diagnostics = CtrlAppDiagnostics();
CtrlAppHandlers(diagnostics).install(); // collects Flutter and platform errors
final signals = await CtrlAppIntegrity.detect();
final permission = await CtrlAppPush.requestPermission();
The sign-in classes have short, generic names (Loading, Offline, ...), so
they live in their own import: import 'package:ctrlapp/auth.dart';
Requirements #
- Flutter 3.10 or newer, Dart 3
- Android 5.0 (API 21) or newer, iOS 12 or newer
Help #
- Documentation: https://ctrlapp.dev/docs
- Questions: contact@ctrlapp.dev
MIT license.