SDKs

Flutter Integration

GitHub starsGitHub forksView Source

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:

PropertyTypeDescription
maintenanceActiveboolWhether maintenance mode is active
forceUpgradeboolWhether a force update is required
messageString?Custom message from the Apptuner dashboard
androidSourceString?Play Store URL for the update button
appleSourceString?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.'),
      ),
    );
  }
}