Floating App Icon
A production-ready Flutter plugin that displays the application's launcher icon as a floating overlay above other applications on Android (similar to Messenger Chat Heads). When tapped, the icon brings the application back to the foreground.
This is ideal for drivers, delivery agents, field service workers, and kiosk applications.
Features
- Android-Only Overlay: Creates a system overlay using
WindowManagerandTYPE_APPLICATION_OVERLAY. - Launcher Icon Display: Extracts and displays the application's actual launcher icon automatically.
- Draggable Icon: Drags smoothly across the screen, respects screen boundaries, and persists the last position during the session.
- One-Tap Restore: Restores the existing activity stack or launches the MainActivity to bring the app to the foreground on tap.
- Foreground Service Support: Uses a Foreground Service to prevent Android from terminating the overlay when the app is in the background.
Installation
Add the plugin to your pubspec.yaml:
dependencies:
floating_app_icon:
path: /path/to/plugin # Or standard pub.dev reference
Android Configuration
1. Permissions
The plugin declares the following permissions in its AndroidManifest.xml (which will be merged into your app automatically):
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
2. Foreground Service (Android 14+ / API 34+)
Starting from Android 14 (API 34), services using overlays require a Foreground Service Type. This plugin declares and starts the service under the specialUse type.
Important
When uploading your app to the Google Play Console targeting Android 14+, you must declare a description for using the specialUse Foreground Service type on the App Content page.
Use a description similar to: "The app displays a persistent floating overlay launcher icon to allow field workers to quickly return to the active tracking/delivery workspace from any other application."
API Usage
Import the package in your Dart code:
import 'package:floating_app_icon/floating_app_icon.dart';
Check Permission
Check if the application has permission to draw overlays:
bool granted = await FloatingAppIcon.hasPermission();
Request Permission
Redirect the user to the system settings page to grant overlay permission:
await FloatingAppIcon.requestPermission();
Show Overlay Icon
Starts the overlay service and shows the launcher icon near the right side of the screen (or at the last dragged position):
await FloatingAppIcon.show();
Hide Overlay Icon
Stops the overlay service and removes the icon:
await FloatingAppIcon.hide();
Check Overlay Status
Check if the floating app icon is currently active and showing:
bool showing = await FloatingAppIcon.isShowing();
Complete Example
import 'package:flutter/material.dart';
import 'package:floating_app_icon/floating_app_icon.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _hasPermission = false;
bool _isShowing = false;
@override
void initState() {
super.initState();
_checkStatus();
}
Future<void> _checkStatus() async {
final granted = await FloatingAppIcon.hasPermission();
final showing = await FloatingAppIcon.isShowing();
setState(() {
_hasPermission = granted;
_isShowing = showing;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Floating App Icon')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Permission Granted: $_hasPermission'),
Text('Overlay Showing: $_isShowing'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await FloatingAppIcon.requestPermission();
},
child: const Text('Request Permission'),
),
ElevatedButton(
onPressed: () async {
await FloatingAppIcon.show();
_checkStatus();
},
child: const Text('Show Overlay'),
),
ElevatedButton(
onPressed: () async {
await FloatingAppIcon.hide();
_checkStatus();
},
child: const Text('Hide Overlay'),
),
],
),
),
),
);
}
}