SDKs
Flutter Integration
Integrate Apptuner into your Flutter application with just a few lines of code. The SDK automatically handles force update and maintenance mode checks — including built-in overlay screens that block user interaction when required.
1. Installation
Add apptuner to your pubspec.yaml:
dependencies:
apptuner: ^1.0.0
Or run:
flutter pub add apptuner
2. Initialize the SDK
Initialize Apptuner in your main() before calling runApp. The SDK only requires your API Key — the platform and app version are detected automatically.
import 'package:apptuner/apptuner.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize apptuner with your API Key
await apptuner.instance.init(apiKey: 'YOUR_API_KEY');
runApp(const MyApp());
}
Note:
init()fetches the latest config from the Apptuner API on startup. The SDK automatically sends the current platform (android / ios) and app version with every request.
3. Add the ApptunerWrapper
Wrap your app with ApptunerWrapper to automatically display a force update or maintenance overlay when needed. The easiest way is using the builder property of MaterialApp:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
builder: (context, child) {
return ApptunerWrapper(
child: child!,
);
},
home: const HomeScreen(),
);
}
}
The wrapper automatically:
- Displays a maintenance screen when maintenance mode is active.
- Displays a force update dialog when a required update is available.
- Re-checks the config every time the app is resumed from background.
4. Customization
Custom Styling
Use ApptunerStyle to customize the appearance of the built-in overlay screens:
ApptunerWrapper(
style: const ApptunerStyle(
backgroundGradient: LinearGradient(
colors: [Colors.purple, Colors.blue],
),
maintenanceIconColor: Colors.red,
updateIconColor: Colors.orange,
maintenanceIconBackgroundColor: Colors.black,
updateIconBackgroundColor: Color(0xFF5A3010),
titleStyle: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
messageStyle: TextStyle(fontSize: 16, color: Colors.grey),
buttonGradient: LinearGradient(
colors: [Colors.orange, Colors.deepOrange],
),
buttonTextStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
child: child!,
);
Fully Custom UI with Builder
For complete control over the overlay, use the builder parameter to render your own widget when maintenance or a force update is active:
ApptunerWrapper(
builder: (context, config) {
if (config.forceUpgrade) {
return MyCustomUpdateScreen(message: config.message);
}
return MyCustomMaintenanceScreen(message: config.message);
},
child: child!,
);
The TunerConfig object passed to the builder contains:
| Property | Type | Description |
|---|---|---|
maintenanceActive | bool | Whether maintenance mode is active |
forceUpgrade | bool | Whether a force update is required |
message | String? | Custom message from the Apptuner dashboard |
androidSource | String? | Play Store URL for the update button |
appleSource | String? | App Store URL for the update button |
5. Manual Update Check
You can manually trigger a config re-fetch at any time — for example, from a pull-to-refresh or a button tap:
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
apptuner.instance.checkUpdate();
},
);
6. Access Config Directly
If you need to read the config values without using the wrapper (e.g., for custom logic), you can access them directly on the singleton:
final tuner = apptuner.instance;
if (tuner.isMaintenance) {
// App is in maintenance mode
}
if (tuner.forceUpgrade) {
// A required update is available
}
// Read individual fields
print(tuner.message);
print(tuner.androidSource);
print(tuner.appleSource);
You can also listen for config changes since Apptuner extends ChangeNotifier:
apptuner.instance.addListener(() {
final config = apptuner.instance.config;
// React to config changes
});
Full Example
import 'package:apptuner/apptuner.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await apptuner.instance.init(apiKey: 'YOUR_API_KEY');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App Tuner Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
builder: (context, child) {
return ApptunerWrapper(
style: const ApptunerStyle(),
child: child!,
);
},
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My App'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
apptuner.instance.checkUpdate();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Checked for updates')),
);
},
),
],
),
body: const Center(
child: Text('apptuner is running in the background.\n'
'If maintenance or force update is active, an overlay will appear.'),
),
);
}
}