Flutter Floating Window

A Flutter plugin for creating and managing floating overlay windows on Android. This plugin allows you to create draggable, resizable floating windows that can stay on top of other applications.

Features

  • ✅ Create multiple floating windows
  • ✅ Draggable windows with touch support
  • ✅ Customizable window size and position
  • ✅ Window visibility control
  • ✅ Data communication between main app and floating windows
  • ✅ Event handling (window created, moved, closed, etc.)
  • ✅ Permission management for overlay access
  • ✅ Works when main app is closed
  • ✅ Customizable window appearance

Requirements

  • Flutter SDK: >=3.0.0
  • Android SDK: API level 23 (Android 6.0) or higher
  • Kotlin: 1.7.10 or higher

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_floating_window:
    path: ../  # Use appropriate path or pub.dev when published

Then run:

flutter pub get

Android Setup

1. Permissions

Add the following permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

2. Service Declaration

Add the floating window service and boot receiver to your AndroidManifest.xml inside the <application> tag:

<service
    android:name="com.example.flutter_floating_window.FloatingWindowService"
    android:enabled="true"
    android:exported="false" />

<receiver
    android:name="com.example.flutter_floating_window.BootReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="1000">
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
        <action android:name="android.intent.action.PACKAGE_REPLACED" />
        <data android:scheme="package" />
    </intent-filter>
</receiver>

3. Queries (Android 11+)

For Android 11 and above, add package visibility queries:

<queries>
    <intent>
        <action android:name="android.intent.action.MAIN" />
    </intent>
</queries>

Usage

Basic Example

import 'package:flutter/material.dart';
import 'package:flutter_floating_window/flutter_floating_window.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    _initializeFloatingWindow();
  }

  Future<void> _initializeFloatingWindow() async {
    // Listen to floating window events
    FloatingWindowManager.eventStream.listen((event) {
      print('Floating window event: ${event.type}');
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Floating Window Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _checkPermission,
                child: Text('Check Permission'),
              ),
              ElevatedButton(
                onPressed: _requestPermission,
                child: Text('Request Permission'),
              ),
              ElevatedButton(
                onPressed: _createFloatingWindow,
                child: Text('Create Floating Window'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _checkPermission() async {
    final hasPermission = await FloatingWindowManager.hasOverlayPermission();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Overlay permission: ${hasPermission ? "Granted" : "Denied"}'),
      ),
    );
  }

  Future<void> _requestPermission() async {
    final granted = await FloatingWindowManager.requestOverlayPermission();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Permission ${granted ? "granted" : "denied"}'),
      ),
    );
  }

  Future<void> _createFloatingWindow() async {
    final config = FloatingWindowConfig(
      width: 300,
      height: 200,
      title: 'My Floating Window',
      isDraggable: true,
      showCloseButton: true,
    );

    final windowId = await FloatingWindowManager.createWindow(config);
    if (windowId != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Window created: $windowId')),
      );
    }
  }
}

Advanced Configuration

final config = FloatingWindowConfig(
  width: 400,
  height: 300,
  initialX: 100,
  initialY: 200,
  isDraggable: true,
  isResizable: false,
  stayOnTop: true,
  backgroundColor: Colors.blue.value,
  cornerRadius: 12.0,
  title: 'Advanced Window',
  showCloseButton: true,
  focusable: true,
  opacity: 0.9,
);

final windowId = await FloatingWindowManager.createWindow(config);

Window Management

// Move window
await FloatingWindowManager.moveWindow(windowId, 150, 250);

// Resize window
await FloatingWindowManager.resizeWindow(windowId, 350, 250);

// Hide/Show window
await FloatingWindowManager.setWindowVisibility(windowId, false);

// Send data to window
await FloatingWindowManager.sendDataToWindow(windowId, {
  'title': 'Updated Title',
  'content': 'New content for the window'
});

// Close window
await FloatingWindowManager.closeWindow(windowId);

// Close all windows
await FloatingWindowManager.closeAllWindows();

Event Handling

FloatingWindowManager.eventStream.listen((event) {
  switch (event.type) {
    case FloatingWindowEventType.windowCreated:
      print('Window created: ${event.windowId}');
      break;
    case FloatingWindowEventType.windowClosed:
      print('Window closed: ${event.windowId}');
      break;
    case FloatingWindowEventType.windowMoved:
      print('Window moved: ${event.data}');
      break;
    case FloatingWindowEventType.windowClicked:
      print('Window clicked: ${event.windowId}');
      break;
    case FloatingWindowEventType.error:
      print('Error: ${event.data?['errorMessage']}');
      break;
  }
});

API Reference

FloatingWindowManager

Methods

  • hasOverlayPermission()Future<bool>
  • requestOverlayPermission()Future<bool>
  • createWindow(FloatingWindowConfig config)Future<String?>
  • closeWindow(String windowId)Future<bool>
  • updateWindow(String windowId, FloatingWindowConfig config)Future<bool>
  • moveWindow(String windowId, int x, int y)Future<bool>
  • resizeWindow(String windowId, int width, int height)Future<bool>
  • setWindowVisibility(String windowId, bool visible)Future<bool>
  • getActiveWindows()Future<List<String>>
  • getWindowConfig(String windowId)Future<FloatingWindowConfig?>
  • sendDataToWindow(String windowId, Map<String, dynamic> data)Future<bool>
  • closeAllWindows()Future<void>
  • windowExists(String windowId)Future<bool>

Properties

  • eventStreamStream<FloatingWindowEvent>

FloatingWindowConfig

Properties

  • width (int): Window width in pixels
  • height (int): Window height in pixels
  • initialX (int?): Initial X position
  • initialY (int?): Initial Y position
  • isDraggable (bool): Enable drag functionality
  • isResizable (bool): Enable resize functionality
  • stayOnTop (bool): Keep window on top
  • backgroundColor (int?): Background color
  • cornerRadius (double): Corner radius for rounded corners
  • title (String?): Window title
  • showCloseButton (bool): Show close button
  • focusable (bool): Window can receive focus
  • opacity (double): Window opacity (0.0 to 1.0)

FloatingWindowEvent

Properties

  • type (FloatingWindowEventType): Event type
  • windowId (String): Window identifier
  • timestamp (DateTime): Event timestamp
  • data (Map<String, dynamic>?): Additional event data

Troubleshooting

Common Issues

  1. Permission Denied Error

    • Ensure SYSTEM_ALERT_WINDOW permission is declared in AndroidManifest.xml
    • Request overlay permission before creating windows
    • Check if permission is granted using hasOverlayPermission()
  2. Window Not Appearing

    • Verify overlay permission is granted
    • Check if window dimensions are valid (> 0)
    • Ensure the device supports overlay windows
  3. Build Errors

    • Make sure Android SDK version is 23 or higher
    • Verify Kotlin version compatibility
    • Clean and rebuild the project
  4. Service Not Starting

    • Ensure service is declared in AndroidManifest.xml
    • Check if FOREGROUND_SERVICE permission is granted
    • Verify service class path is correct

Debug Tips

  • Enable debug logging to see detailed error messages
  • Use flutter logs to monitor plugin activity
  • Test on different Android versions and devices
  • Check system settings for overlay permissions

Limitations

  • Android only (iOS does not support system overlay windows)
  • Requires API level 23 (Android 6.0) or higher
  • Maximum of 10 concurrent floating windows
  • Limited customization options for window content
  • Performance may vary on different devices

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you encounter any issues or have questions, please file an issue on our GitHub repository.